Задать вопрос
Ответы пользователя по тегу Базы данных
  • Как оптимизировать запрос SELECT COUNT?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    SELECT COUNT(*) FROM - перебрать все строки.
    SELECT COUNT(id) FROM - перебрать все строки, в которых указанное поле (в данном случае id) имеет значение отличное от NULL.

    Без указания полей - наиболее оптимальный вариант для PostgreSQL:
    SELECT COUNT(*) FROM table WHERE field = value
    https://wiki.postgresql.org/wiki/Slow_Counting

    Если все совсем плохо, то как вариант, можно сделать собственный счетчик.

    Вот вырезка из PostgreSQL Wiki на русском языке:
    Почему "SELECT count(*) FROM bigtable;" работает медленно?
    Потому что не используется индекс. PostgreSQL выполняет проверку видимости каждой записи и таким образом производит последовательное сканирование всей таблицы. Если вы хотите, вы можете отслеживать количество строк в таблице с помощью триггеров, но это вызовет замедление при операциях записи в таблицу.
    Вы можете получить некоторую оценку. Колонка reltuples в таблице pg_class содержит информацию из результата выполнения последнего оператора ANALYZE на эту таблицу. На большой таблице, точность этого значения составляет тысячные доли процента, что вполне достаточно для многих целей.
    "Точный" результат count, часто не будет точным долгое время в любом случае; из-за конкурентности MVCC, count будет точным только на момент вызова запущенного запроса SELECT count(*) (или ограничиваться уровнями изоляции транзакций данной транзакции), и может потерять актуальность уже в момент завершения запроса. При постоянной работе транзакций, изменяющий таблицу, два вызова count(*), которые завершатся в одно и то же время могут показать разные значения, если изменяющая транзакция завершилась между их вызовами.
    https://wiki.postgresql.org/wiki/Часто_Задаваемые_...
    Ответ написан
    1 комментарий
  • Как правильно работать с большой базой через C# + MsSQLL?

    AlekseyNemiro
    @AlekseyNemiro
    full-stack developer
    Если есть возможность, лучше удалять данные полностью, а потом добавлять новые при помощи SqlBulkCopy.

    Таким образом можно будет большие, я бы даже сказал огромные, объемы данных помещать в базу за короткий промежуток времени. Лучше всего не все сразу, а частями. Например, если 5 000 000 записей, то пачкой 10 000, или по 1 000. Зависит от объема данных и вероятности возникновения ошибок в процессе переноса. Если там простые данные, типа чисел, то можно их одним махом залить в базу. Если пачка будет слишком большой, серверу может памяти не хватить на её обработку.

    Быстро удалить данные можно при помощи инструкции TRUNCATE TABLE [имя таблицы], но не всегда просто, зависит от связей.

    Организовать обновление будет сложнее. Лучше всего выбирать пачку данных, обрабатывать и отправлять обратно на сервер. Размер пачки необходимо определять в зависимости от ресурсов сервера и объема данных в пачке.
    Запросы при обновлении должны быть максимально простыми. Можно использовать XML, но тоже очень простой и небольших объемов, а в идеале обойтись без этого. Чем все будет проще, тем быстрее будет работать. Циклы и курсоры в запросах стараться не использовать.

    Проверку наличия данных можно реализовать на стороне приложения, а не базы. Практика показывает, что это работает быстрее. Перед обновлением можно получить только идентификаторы данных (в DataTable). Далее проверять, есть необходимый идентификатор в DataTable или нет. Чтобы было проще, можно обновлять все данные, если идентификатор будет найден. А если нет, то добавлять данные. Это позволит не делать EXISTS на стороне SQL Server и обновление можно будет выполнить пачкой. Если использовать XML, то примерно так это может выглядеть:

    -- временная таблица
    -- в моем примере всего два поля id и value
    CREATE TABLE #tmp
    (
      num int primary key identity,
      id bigint,
      value nvarchar(max)
    );
    
    -- структура xml:
    -- <items>
    -- <item id="123">значение</item>
    -- <item id="456">значение</item>
    -- </items>
    
    -- переносим xml в таблицу
    INSERT INTO #tmp
    SELECT 
    ISNULL(n.value('@id', 'bigint'), 0), -- идентификатор существующих данных
    n.value('.', 'nvarchar(max)') -- значение данных
    FROM @xml.nodes('/items/item') AS node(n);
    
    -- обновляем записи в таблице [table], у которых есть идентификатор
    UPDATE a SET value = b.value
    FROM table AS a 
    INNER JOIN #tmp AS b ON a.id = b.id
    WHERE b.id <> 0;
    
    -- добавляем записи, у которых нет идентификатора
    INSERT INTO table (value)
    SELECT value FROM #tmp AS b WHERE b.id = 0;
    
    -- удаляем временную таблицу
    DROP TABLE #tmp;

    Если есть возможность, лучше обойтись без XML, JOIN-ов и всяких ISNULL.

    Базу необходимо правильно настроить. Добавить индексы, где будет уместно. Но не переборщить и учитывать, что они (индексы) могут фрагментироваться и сильно замедлять работу базы. Про фрагментацию самой базы, тоже не стоит забывать. Если будете удалять большие объемы данных, то не забывайте выполнять резервное копирование и сжатие базы.

    Соединения с базой лучше использовать одноразовые. Т.е. открыл, выполнил запрос, закрыл и так далее. Если использовать одно соединение, то со временем скорость выполнения запросов будет существенно снижаться.

    Универсального решения подобных задач нет, но это примерно то, от чего можно отталкиваться.
    Ответ написан
    9 комментариев