Конференция "Базы" » Копирование таблицы из одной базы в другую [firebird]
 
  • ford © (09.10.15 10:53) [0]
    Есть две БД, под управлением firebird. Необходимо сделать зеркальную копию одной таблицы из одной БД в другую. структура и имена таблиц идентичны в двух БД.
    Сделал ХП:

    SET TERM ^ ;
    CREATE PROCEDURE STATI_COPY (
       CONNECTION Varchar(300) )
    AS
     declare variable HEADER Varchar(250);
     declare variable PAGENUM Varchar(50);
     declare variable NUMBER Varchar(40);
     declare variable ID Integer;
     declare variable STATUS Char(1);
     declare variable IsExists Integer;
    begin
    /*из таблицы главной бд, копируем в подчиненную*/
    FOR
       EXECUTE STATEMENT ('select ID,NUMBER,PAGENUM,HEADER,STATUS from stati')
         ON EXTERNAL :Connection
         WITH AUTONOMOUS TRANSACTION
         AS USER 'sysdba' PASSWORD 'masterkey'
       INTO :ID,:NUMBER,:PAGENUM,:HEADER,:STATUS
     DO
       if (not exists(select t.id from stati t where t.id=:ID)) Then
         insert into stati (ID,NUMBER,PAGENUM,HEADER,STATUS)
          values (:ID,:NUMBER,:PAGENUM,:HEADER,:STATUS);

    end^
    SET TERM ; ^




    Собственно все работает, но так как с подчиненной базой тоже идет работа, то в таблице накапливаются "лишние" записи. Для их удаления в этой же процедуре я добавил код:


    /*из таблицы в подчиненной бд удаляем лишнии записи*/
    FOR
       select t.id from stati t into :ID
    DO
      begin
        Execute statement ('select count(r.id) from stati r where r.id=?')(:ID)
         ON EXTERNAL :Connection
         WITH AUTONOMOUS TRANSACTION
         AS USER 'sysdba' PASSWORD 'masterkey'
         INTO :IsExists;
        if (:IsExists=0) Then
          begin
            delete from stati where id=:ID;
          end  
      end



    И процедура копирования просто зависает, или просто очень долго работает, т.к. обычно выполнение занимало максимум минут 15-20. после дополнения в виде удаления ненужных записей, я прождал сутки, но процедура так и не завершилась.
    Окончательный вариант выглядит так


    SET TERM ^ ;
    CREATE PROCEDURE STATI_COPY (
       CONNECTION Varchar(300) )
    AS
     declare variable HEADER Varchar(250);
     declare variable PAGENUM Varchar(50);
     declare variable NUMBER Varchar(40);
     declare variable ID Integer;
     declare variable STATUS Char(1);
     declare variable IsExists Integer;
    begin
    /*из таблицы главной бд, копируем в подчиненную*/
    FOR
       EXECUTE STATEMENT ('select ID,NUMBER,PAGENUM,HEADER,STATUS from stati')
         ON EXTERNAL :Connection
         WITH AUTONOMOUS TRANSACTION
         AS USER 'sysdba' PASSWORD 'masterkey'
       INTO :ID,:NUMBER,:PAGENUM,:HEADER,:STATUS
     DO
       if (not exists(select t.id from stati t where t.id=:ID)) Then
         insert into stati (ID,NUMBER,PAGENUM,HEADER,STATUS)
          values (:ID,:NUMBER,:PAGENUM,:HEADER,:STATUS);

    /*из таблицы в подчиненной бд удаляем лишнии записи*/
    FOR
       select t.id from stati t into :ID
    DO
      begin
        Execute statement ('select count(r.id) from stati r where r.id=?')(:ID)
         ON EXTERNAL :Connection
         WITH AUTONOMOUS TRANSACTION
         AS USER 'sysdba' PASSWORD 'masterkey'
         INTO :IsExists;
        if (:IsExists=0) Then
          begin
            delete from stati where id=:ID;
          end  
      end

    end^
    SET TERM ; ^




    Подскажите, в чем причина такого зависания?
  • ford © (09.10.15 11:22) [1]
    Забыл добавить, что процедура в подчиненной БД. т.е. Все execute statement идут к главной БД
  • Кщд © (09.10.15 13:17) [2]
    1. Записать данные из главной БД во временную таблицу в подчиненной БД;
    2. Сравнить данные из временной таблицы с имеющимися данными, выполнив при необходимости insert/delete;
    3. Очистить временную таблицу.
  • ford © (11.10.15 12:50) [3]

    > 1. Записать данные из главной БД во временную таблицу в
    > подчиненной БД;
    2. Сравнить данные из временной таблицы с имеющимися данными, выполнив при необходимости insert/delete;

    Зачем? если я и так делаю выборку из главной БД и сравниваю записи, есть такая в подчиненной или нет. если нет, то insert.
    Следующий цикл проверяет записи из подчиненной с главной, если такой нет в главной, то delete.
    Зависает на втором цикле, проверки записей в подчиненной на наличие в главной.

    И еще, записей в главной БД более 9млн., каждый день прибавляется по 10-30тыс. , т.е. копировать всю таблицу во временную, это ооочень долгий процесс получается.
  • ухты © (11.10.15 13:26) [4]
    зачем все копировать? достаточно только новые
  • Кщд © (12.10.15 08:13) [5]
    >ford ©   (11.10.15 12:50) [3]
    >Зачем?
    затем, что так гораздо правильнее и существенно быстрее

    >т.е. копировать всю таблицу во временную, это ооочень долгий процесс >получается.
    приведенный Вами код ДВАЖДЫ выполняет операции, необходимые для копирования (за исключением insert во временную)

    >И еще, записей в главной БД более 9млн., каждый день прибавляется >по 10-30тыс.
    собственно, на это ответили в "ухты ©   (11.10.15 13:26) [4] "
  • ford © (13.10.15 11:29) [6]
    Что то я Вас не совсем понял.
    В моей ХП
    первый цикл, открывает таблицу в главной БД, берёт каждую запись, и проверяет наличие записи с таким же ID в подчиненной, если таковой нет, то делает insert.
    Второй цикл, открывает таблицу в подчиненной и проверяет каждую запись на наличие этого ID в главной, если такового нет, то делает delete этой записи.

    Копировать 9млн записей во временную таблицу, это не быстрее, поверьте! первичное копирование из главной в подчиненную (т.е. первый цикл ХП), заняло более 15 часов!!!


    > ухты ©   (11.10.15 13:26) [4]
    >
    > зачем все копировать? достаточно только новые


    Так я и не копирую ВСЕ, я проверяю для каждой наличие в подчиненной, и копирую только те которых нет!!

    ...
    DO
      if (not exists(select t.id from stati t where t.id=:ID)) Then
        insert into stati (ID,NUMBER,PAGENUM,HEADER,STATUS)
         values (:ID,:NUMBER,:PAGENUM,:HEADER,:STATUS);


    if (not exists   вам ни очем не говорит?


    > Кщд ©   (12.10.15 08:13) [5]
    приведенный Вами код ДВАЖДЫ выполняет операции, необходимые для копирования (за исключением insert во временную)


    ГДЕ!? Где Вы увидели временную таблицу? и где ДВАЖДЫ выполнение операций?
    первый цикл, проверяет наличие новых записей.
    второй, удаляет не нужные.

    Приведите код "правильного" зеркалирования таблиц, в двух разных БД!
  • ухты © (14.10.15 02:13) [7]
    Вам про Фому а вы про Ерему.
    Разговор о том что не надо каждый раз перепахивать то что уже перепахано.
    Вы каждый раз ходите по всей таблице, а зачем? если вам надо только то что изменилось с предыдущего раза? Те что уже скопировали, вы повторно копировать не будете, так и проверять их не зачем.

    Вам средства репликации точно не подходят?
  • Кщд © (14.10.15 06:26) [8]
    >ГДЕ!? Где Вы увидели временную таблицу?
    у Вас - нигде
    я про это и не писал.

    >и где ДВАЖДЫ выполнение операций?

    1.
    >EXECUTE STATEMENT ('select ID,NUMBER,PAGENUM,HEADER,STATUS from stati')
    это полный проход по таблице в главной БД
    т.е. Вы УЖЕ выбрали все данные из главной

    2.
    >Execute statement ('select count(r.id) from stati r where r.id=?')(:ID)
    это ещё один проход по таблице из главной

    1 + 1 = 2, т.е. дважды

    >Приведите код "правильного" зеркалирования таблиц, в двух разных БД!
    что именно непонятно в [2]?
    либо включайте мозг, либо $100
  • ford © (14.10.15 10:56) [9]

    > Вы каждый раз ходите по всей таблице, а зачем? если вам
    > надо только то что изменилось с предыдущего раза?

    Да, но как я сделаю запрос из одной БД в другую, чтобы выбрать записи только те которых нет в этой?
    Firebird такого не позволяет вроде. Через Execute statement, используя ON EXTERNAL <строка соединения с другой БД>   WITH AUTONOMOUS TRANSACTION     AS USER 'sysdba' PASSWORD 'masterkey'
    я могу выбрать записи, но как я добавлю к этому запросу проверку типа not exists в другой БД?
    потому и приходится проходится по всей выборке.


    > Кщд ©   (14.10.15 06:26) [8]
    1.
    >EXECUTE STATEMENT ('select ID,NUMBER,PAGENUM,HEADER,STATUS from stati')
    это полный проход по таблице в главной БД
    т.е. Вы УЖЕ выбрали все данные из главной

    2.
    >Execute statement ('select count(r.id) from stati r where r.id=?')(:ID)
    это ещё один проход по таблице из главной


    Ну дык, в первом цикле я проверяю ест ли запись из главной БД в подчиненной и если её нет. то добавляю. А во втором, я прохожусь по записям подчиненной и проверяю её наличие в главной, и если её нет, то удаляю.
    Как я по вашему это запихну в один цикл?
    например в главной БД в таблице есть записи с ID = 1,2,3,4,5
    в подчиненной есть с ID = 1,2,4,5,6,7
    в первом цикле, невозможно найти записи подчиненной таблицы с ID 6,7 которые необходимо удалить, т.к. на каждом шаге цикла мы получим записи с ID 1,2,3,4,5 но ни как не узнаем о существовании с ID 6,7 т.к. в главной таблице их просто нет!


    > что именно непонятно в [2]?

    Вы видимо плохо прочитали ответ
    >>Копировать 9млн записей во временную таблицу, это не быстрее, поверьте! первичное копирование из главной в подчиненную (т.е. первый цикл ХП), заняло более 15 часов!!!

    В предлагаете скопировать 9 млн записей из главной БД в подчиненную в течении 15 часов, а потом конечно выполнить два запроса
    типа:
    insert into stati select t.* from stati_temp t where not exists (select k.id from stati k where k.id=t.id)
    и второй
    delete from stati where not exists (select k.id from stati_temp k where r.id=id)

    или у вас есть способ быстрого копирования таблицы из одной БД в другую?
    это не Oravle с его tablespaces, и не MySQL, это firebird.
    В firebird нет варианта
    select t.*,k.* from bd1.stati t, bd2.stati k where t.id=k.id


    > либо включайте мозг, либо $100

    алаверды - внимательнее читайте вопрос.
  • Кщд © (14.10.15 13:05) [10]
    >ford ©   (14.10.15 10:56) [9]
    в первом посте Вы утверждали, что "т.к. обычно выполнение занимало максимум минут 15-20."

    затем: " первичное копирование из главной в подчиненную (т.е. первый цикл ХП), заняло более 15 часов!!!"

    где правда?

    1. читать вы не умеете;
    2. понимать прочитанное не способны;
    3. минимальной квалификацией (судя по приведенному коду) для работы с БД не обладаете.
    => наймите программиста

    поскорее давайте мне, пожалуйста, $100, и напишу код, который
    сделает нужные Вам insert/delete в подчиненной таблице за так желаемые Вами "15-20мин"
  • ford © (14.10.15 14:05) [11]

    > где правда?

    нда...., поясняю Вам, что такое первичное копирование: это когда мы выполняем процедуру копирования в первый раз, т.е. таблица-приемник пуста! и туда загоняются 9млн. записей.
    Этот процесс выполняется более 15 часов.
    Далее, если внимательно смотрели код, который я выкладываю, то могли заметить, что там стоит условие, так вот, при последующем запуске процедуры, копируется не 9млн. записей, а только те, которые внесли после первичного запуска. Вот второй и последующие разы, время копирования мин. 15-20.


    > 3. минимальной квалификацией (судя по приведенному коду)
    > для работы с БД не обладаете.
    > => наймите программиста
    поскорее давайте мне, пожалуйста, $100, и напишу код, который
    сделает нужные Вам insert/delete в подчиненной таблице за так желаемые Вами "15-20мин"

    алаверды - если Вы даже не читаете вопрос и не можете вникнуть в код, то какие 100$? Вам, судя по всему, еще учиться и учиться, а пока Ваш уровень это Hello world на VBA
  • ухты © (14.10.15 14:40) [12]

    > Да, но как я сделаю запрос из одной БД в другую, чтобы выбрать
    > записи только те которых нет в этой?
    При помощи установки соответствующих условий, очевидно.
  • ford © (14.10.15 15:12) [13]

    > При помощи установки соответствующих условий, очевидно.

    В условиях данной задачи, какие условия я могу поставить для execute statement? Учитывая что, выполняется запрос в другой БД.
    Т.е. ХП находится в базе '192.168.0.250:otdel', а запрос к базе '192.168.0.254:dummy'


    FOR
      EXECUTE STATEMENT ('select ID,NUMBER,PAGENUM,HEADER,STATUS from stati')
        ON EXTERNAL '192.168.0.254:dummy'
        WITH AUTONOMOUS TRANSACTION
        AS USER 'sysdba' PASSWORD 'masterkey'
      INTO :ID,:NUMBER,:PAGENUM,:HEADER,:STATUS
    DO
      if (not exists(select t.id from stati t where t.id=:ID)) Then
        insert into stati (ID,NUMBER,PAGENUM,HEADER,STATUS)
         values (:ID,:NUMBER,:PAGENUM,:HEADER,:STATUS);


    т.е. каким образом к запросу EXECUTE STATEMENT ('select ID,NUMBER,PAGENUM,HEADER,STATUS from stati') , Вы собираетесь указать список записей, которые выбирать не надо?

    Если вы не в курсе, что такое execute statement, то  вот ссылка на справочник
    http://firebirdsql.su/doku.php?id=execute_statement

    Ребята ухты и Кщд, если Вы не знаете, то не пишите ерунды и общих фраз. Если просто хотите денег за ответы, то Вы не на том форуме пасетесь.
  • Кщд © (14.10.15 15:25) [14]
    >ford ©   (14.10.15 15:12) [13]
    желаю счастья!))
  • ford © (14.10.15 15:28) [15]

    > Кщд ©   (14.10.15 15:25) [14]

    И Вам, не хворать :)
  • ухты © (14.10.15 16:02) [16]
    нов от тут вы ставите условия  :

    > Execute statement ('select count(r.id) from stati r where
    > r.id=?')(:ID)
    или в чем проблема? не знаете что поставить, изучайте свою структуру делайте изменения добивайтесь результата.
  • Кщд © (14.10.15 16:21) [17]
    >ухты ©   (14.10.15 16:02) [16]
    бесполезняк это всё)
    пациент даже не понимает, что здесь:
    EXECUTE STATEMENT ('select ID,NUMBER,PAGENUM,HEADER,STATUS from stati')

    он каждый раз (при каждом запуске) получает все 9 000 000 записей в подчиненную БД из главной

    про дальнейшую оптимизацию (триггеры на изменение в главной БД и подчиненной со складированием ID измененных записей в спец. таблицы с тем, чтобы не ворочать 9*10^6 записей каждый раз) и говорить нечего

    пациент скорее мертв, чем наоборот))
  • ford © (16.10.15 10:44) [18]

    > про дальнейшую оптимизацию (триггеры на изменение в главной
    > БД и подчиненной со складированием ID измененных записей
    > в спец. таблицы с тем, чтобы не ворочать 9*10^6 записей
    > каждый раз) и говорить нечего

    Прикнь, знаю и использую :)
    Но, есть задача, которая описанна в вопросе и её решение. Медленное, но решение.
    К сожалению, Кщд, как я понял из ваших ответов, Ваш уровень знания не позволяет оптимизировань её решение.
    Говорить пустые, общие фразы, это ваш конек :)
  • Кщд © (19.10.15 10:49) [19]
    >ford ©   (16.10.15 10:44) [18]
    >К сожалению, Кщд, как я понял из ваших ответов, Ваш уровень знания не позволяет оптимизировань её решение.
    >Говорить пустые, общие фразы, это ваш конек :)
    увы, это так(((
    но я азартен, черт подери!
    давайте условимся так:
    1. за сутки переучиваюсь с VBA для Excel на PSQL;
    2. пишу и выкладываю код, выполняющий Вашу задачу (в рамках озвученных в этой ветке условий), здесь;
    3. если - вдруг! - мой код сработает, вы высылаете мне $200.
    если, что верно с вероятностью 99.9997%, код не срабатывает, то я высылаю вам $300 (даже $315 - именно столько завалялось у меня в электронном кошельке).

    легкие деньги, соглашайтесь!)))
  • Труп Васи Доброго © (30.11.15 13:18) [20]
    Всегда умиляли заявления про 100500 мильёнов записей в таблице.
    1. Если ты имеешь права, то меняй структуру и логику работы.
    Пиши весь день ID в "ежедневную" таблицу, а потом, в конце дня, копируй эту маленькую таблицу в "подчинённую". В подчинённой таблице для "местных" действий используй свою таблицу ID, которые при копировании удалятся.
    2.Если никаких прав у тебя нет и если уж тебе ПОСТОЯННО необходимо переливать такую "большую" таблицу, то есть огромное подозрение, что простой "бэкап" здесь, пересылка и "ресторе" там будет намного быстрее и надёжнее твоих изысков.
  • LSP OLEGY (03.12.15 19:26) [21]
    Удалено модератором
 
Конференция "Базы" » Копирование таблицы из одной базы в другую [firebird]
Есть новые Нет новых   [134427   +34][b:0][p:0.003]