Требуется подсчет строк после инструкции SELECT:каков оптимальный подход к SQL?

StackOverflow https://stackoverflow.com/questions/243782

  •  04-07-2019
  •  | 
  •  

Вопрос

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

Подход 1:

SELECT COUNT( my_table.my_col ) AS row_count
  FROM my_table
 WHERE my_table.foo = 'bar'

Тогда

SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'

Или Подход 2

SELECT my_table.my_col, ( SELECT COUNT ( my_table.my_col )
                            FROM my_table
                           WHERE my_table.foo = 'bar' ) AS row_count
  FROM my_table
 WHERE my_table.foo = 'bar'

Я делаю это, потому что мой драйвер SQL (SQL Native Client 9.0) не позволяет мне использовать SQLRowCount в инструкции SELECT, но мне нужно знать количество строк в моем результате, чтобы выделить массив, прежде чем присваивать ему информацию.Использование динамически выделяемого контейнера, к сожалению, не является опцией в этой области моей программы.

Я обеспокоен тем, что может произойти следующий сценарий:

  • ВЫБОР для подсчета происходит
  • Возникает другая инструкция, добавляющая или удаляющая строку
  • Происходит ВЫБОР данных, и внезапно массив оказывается неправильного размера.
    -В худшем случае это приведет к попытке записать данные за пределы массивов и приведет к сбою моей программы.

Запрещает ли подход 2 эту проблему?

Кроме того, будет ли один из двух подходов быстрее?Если да, то какой?

Наконец, есть ли лучший подход, который я должен рассмотреть (возможно, способ указать драйверу возвращать количество строк в результате выбора с использованием SQLRowCount?)

Для тех, кто спрашивал, я использую родной C ++ с вышеупомянутым драйвером SQL (предоставленным Microsoft).)

Это было полезно?

Решение

Есть только два способа быть на 100% уверенным в том, что COUNT(*) и фактический запрос даст согласованные результаты:

  • Объединил в себе COUNT(*) с запросом, как в вашем Подходе 2.Я рекомендую форму, которую вы показываете в своем примере, а не связанную форму подзапроса, показанную в комментарии от kogus.
  • Используйте два запроса, как в вашем Подходе 1, после запуска транзакции в SNAPSHOT или SERIALIZABLE уровень изоляции.

Использование одного из этих уровней изоляции важно, потому что любой другой уровень изоляции позволяет новым строкам, созданным другими клиентами, становиться видимыми в вашей текущей транзакции.Ознакомьтесь с документацией MSDN по SET TRANSACTION ISOLATION для получения более подробной информации.

Другие советы

Если вы используете SQL Server, после вашего запроса вы можете выбрать @@Количество строк функция (или, если ваш результирующий набор может содержать более 2 миллиардов строк, используйте RowCount_Big() Строка count_big() функция).Это вернет количество строк, выбранных предыдущей инструкцией, или количество строк, затронутых инструкцией insert / update /delete.

SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'

SELECT @@Rowcount

Или, если вы хотите включить количество строк в отправляемый результат аналогично подходу № 2, вы можете использовать ИЗБЫТОЧНОЕ предложение.

SELECT my_table.my_col,
    count(*) OVER(PARTITION BY my_table.foo) AS 'Count'
  FROM my_table
 WHERE my_table.foo = 'bar'

Использование предложения OVER будет иметь гораздо лучшую производительность, чем использование подзапроса для получения количества строк.Использование @@RowCount будет иметь наилучшую производительность, потому что не будет никаких затрат на запрос для оператора select @@RowCount

Обновление в ответ на комментарий:В примере, который я привел, было бы указано # строк в разделе, определяемом в данном случае с помощью "PARTITION BY my_table.foo".Значением столбца в каждой строке является число строк с одинаковым значением my_table.foo.Поскольку в вашем примере запроса было предложение "WHERE my_table.foo = 'bar'", все строки в результирующем наборе будут иметь одинаковое значение my_table .foo и, следовательно, значение в столбце будет одинаковым для всех строк и равным (в данном случае) этому числу строк в запросе.

Вот лучший / простой пример того, как включить столбец в каждую строку, который является общим числом строк в результирующем наборе.Просто удалите необязательное предложение Partition By.

SELECT my_table.my_col, count(*) OVER() AS 'Count'
  FROM my_table
 WHERE my_table.foo = 'bar'

Подход 2 всегда будет возвращать количество, соответствующее вашему результирующему набору.

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

SELECT 
  mt.my_row,
 (SELECT COUNT(mt2.my_row) FROM my_table mt2 WHERE mt2.foo = mt.foo) as cnt
FROM my_table mt
WHERE mt.foo = 'bar';

Если вы обеспокоены тем, что количество строк, удовлетворяющих условию, может измениться за несколько миллисекунд с момента выполнения запроса и получения результатов, вы могли бы / должны выполнить запросы внутри транзакции:

BEGIN TRAN bogus

SELECT COUNT( my_table.my_col ) AS row_count
FROM my_table
WHERE my_table.foo = 'bar'

SELECT my_table.my_col
FROM my_table
WHERE my_table.foo = 'bar'
ROLLBACK TRAN bogus

Это всегда возвращало бы правильные значения.

Кроме того, если вы используете SQL Server, вы можете использовать @@ROWCOUNT, чтобы получить количество строк, затронутых последней инструкцией, и перенаправить вывод реальный запрос к временной таблице или табличной переменной, так что вы можете вернуть все целиком, и нет необходимости в транзакции:

DECLARE @dummy INT

SELECT my_table.my_col
INTO #temp_table
FROM my_table
WHERE my_table.foo = 'bar'

SET @dummy=@@ROWCOUNT
SELECT @dummy, * FROM #temp_table

Вот несколько идей:

  • Используйте подход № 1 и измените размер массива для хранения дополнительных результатов или используйте тип, который автоматически изменяет размер по мере необходимости (вы не указываете, какой язык вы используете, поэтому я не могу быть более конкретным).
  • Вы могли бы выполнить оба оператора в подходе № 1 внутри транзакции, чтобы гарантировать, что подсчеты будут одинаковыми оба раза, если ваша база данных поддерживает это.
  • Я не уверен, что вы делаете с данными, но если возможно обработать результаты, не сохраняя их все сначала, это может быть лучшим методом.

Если вы действительно обеспокоены тем, что количество ваших строк изменится между select count и инструкцией select, почему бы сначала не выделить ваши строки во временную таблицу?Таким образом, вы знаете, что будете синхронизированы.

Почему бы вам не поместить свои результаты в вектор?Таким образом, вам не нужно заранее знать размер.

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

Ни один самонастраивающийся драйвер SQL не сообщит вам, сколько строк вернет ваш запрос, прежде чем возвращать строки, потому что ответ может измениться (если только вы не используете транзакцию, что само по себе создает проблемы).

Количество строк не изменится - в Google есть ACID и SQL.

IF (@@ROWCOUNT > 0)
BEGIN
SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'
END

Просто добавляю это, потому что это лучший результат в Google по этому вопросу.В sqlite я использовал это, чтобы получить rowcount .

WITH temptable AS
  (SELECT one,two
   FROM
     (SELECT one, two
      FROM table3
      WHERE dimension=0
      UNION ALL SELECT one, two
      FROM table2
      WHERE dimension=0
      UNION ALL SELECT one, two
      FROM table1
      WHERE dimension=0)
   ORDER BY date DESC)
SELECT *
FROM temptable
LEFT JOIN
  (SELECT count(*)/7 AS cnt,
                        0 AS bonus
   FROM temptable) counter
WHERE 0 = counter.bonus
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top