Нужен счетчик строк после оператора SELECT: каков оптимальный подход SQL? - PullRequest
31 голосов
/ 28 октября 2008

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

Подход 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 9.0) не позволяет мне использовать SQLRowCount в операторе SELECT, но мне нужно знать количество строк в моем результате, чтобы выделить массив, прежде чем присваивать информацию Это. Использование динамически размещенного контейнера, к сожалению, не вариант в этой области моей программы.

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

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

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

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

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

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

Ответы [ 10 ]

29 голосов
/ 28 октября 2008

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

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

SELECT @@Rowcount

Или, если вы хотите, чтобы количество строк, включенных в результат, отправленный аналогично подходу №2, вы можете использовать предложение OVER .

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'
16 голосов
/ 28 октября 2008

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

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

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

3 голосов
/ 28 октября 2008

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

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, чтобы получить количество строк, на которые воздействовал последний оператор, и перенаправить вывод запроса real во временную таблицу или переменную таблицы , так что вы можете вернуть все в целом, и нет необходимости транзакции:

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
3 голосов
/ 28 октября 2008

Подход 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';
1 голос
/ 28 октября 2008

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

1 голос
/ 28 октября 2008

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

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

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

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
0 голосов
/ 02 августа 2010
IF (@@ROWCOUNT > 0)
BEGIN
SELECT my_table.my_col
  FROM my_table
 WHERE my_table.foo = 'bar'
END
0 голосов
/ 28 октября 2008

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

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

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

0 голосов
/ 28 октября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...