Necesita un conteo de filas después de la instrucción SELECT: ¿cuál es el enfoque de SQL óptimo?

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

  •  04-07-2019
  •  | 
  •  

Pregunta

Estoy tratando de seleccionar una columna de una sola tabla (sin uniones) y necesito el recuento del número de filas, idealmente antes de comenzar a recuperar las filas. He venido a dos enfoques que proporcionan la información que necesito.

Enfoque 1:

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

entonces

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

O Enfoque 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'

Estoy haciendo esto porque mi controlador SQL (SQL Native Client 9.0) no me permite usar SQLRowCount en una instrucción SELECT pero necesito saber la cantidad de filas en mi resultado para poder asignar una matriz antes de asignar información a eso. Desafortunadamente, el uso de un contenedor asignado dinámicamente no es una opción en esta área de mi programa.

Me preocupa que pueda ocurrir el siguiente escenario:

  • SELECT para el conteo ocurre
  • Se produce otra instrucción, agregar o eliminar una fila
  • SELECT para datos ocurre y de repente la matriz tiene el tamaño incorrecto.
      -En el peor de los casos, esto intentará escribir datos más allá de los límites de las matrices y bloquear mi programa.

¿El Enfoque 2 prohíbe este problema?

También, ¿Será uno de los dos enfoques más rápido? Si es así, ¿cuál?

Finalmente, ¿hay un mejor enfoque que deba considerar (tal vez una forma de indicar al controlador que devuelva el número de filas en un resultado SELECT utilizando SQLRowCount?)

Para los que preguntaron, estoy usando Native C ++ con el controlador SQL mencionado anteriormente (proporcionado por Microsoft.)

¿Fue útil?

Solución

Solo hay dos formas de estar 100% seguros de que COUNT (*) y la consulta real darán resultados consistentes:

  • Combinó el COUNT (*) con la consulta, como en su Método 2. Recomiendo la forma que se muestra en su ejemplo, no la forma de subconsulta correlacionada que se muestra en el comentario de kogus.
  • Use dos consultas, como en su Método 1, después de iniciar una transacción en el nivel de aislamiento SNAPSHOT o SERIALIZABLE .

El uso de uno de esos niveles de aislamiento es importante porque cualquier otro nivel de aislamiento permite que las nuevas filas creadas por otros clientes se hagan visibles en su transacción actual. Lea la documentación de MSDN en ESTABLECER AISLAMIENTO DE TRANSACCIÓN para más detalles.

Otros consejos

Si está utilizando SQL Server, después de su consulta puede seleccionar @@ RowCount (o si su conjunto de resultados puede tener más de 2 mil millones de filas, use la función RowCount_Big () función). Esto devolverá el número de filas seleccionadas por la declaración anterior o el número de filas afectadas por una declaración de inserción / actualización / eliminación.

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

SELECT @@Rowcount

O si desea que el recuento de filas incluido en el resultado enviado de manera similar al Método 2, puede usar el cláusula OVER .

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

El uso de la cláusula OVER tendrá un rendimiento mucho mejor que el uso de una subconsulta para obtener el recuento de filas. El uso de @@ RowCount tendrá el mejor rendimiento porque no habrá ningún costo de consulta para la declaración select @@ RowCount

Actualización en respuesta al comentario: el ejemplo que dí daría el número de filas en la partición, definida en este caso por " PARTITION BY my_table.foo " ;. El valor de la columna en cada fila es el # de filas con el mismo valor de my_table.foo. Dado que su consulta de ejemplo tenía la cláusula " DÓNDE my_table.foo = 'bar' " ;, todas las filas en el conjunto de resultados tendrán el mismo valor que my_table.foo y, por lo tanto, el valor en la columna será el mismo para todas las filas e igual (en este caso) este es el # de filas en la consulta.

Aquí hay un ejemplo mejor / más simple de cómo incluir una columna en cada fila que es el número total de filas en el conjunto de resultados. Simplemente elimine la cláusula opcional Por partición.

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

El enfoque 2 siempre devolverá un recuento que coincida con el conjunto de resultados.

Le sugiero que vincule la consulta secundaria a su consulta externa, para garantizar que la condición en su cuenta coincida con la condición en el conjunto de datos.

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';

Si le preocupa que la cantidad de filas que cumplan con la condición puede cambiar en unos pocos milisegundos desde la ejecución de la consulta y la recuperación de resultados, podría / debería ejecutar las consultas dentro de una transacción:

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

Esto devolvería los valores correctos, siempre.

Además, si está utilizando SQL Server, puede usar @@ ROWCOUNT para obtener el número de filas afectadas por la última instrucción y redirigir la salida de la consulta real a una tabla o tabla temporal variable, por lo que puede devolver todo por completo, y no necesita una transacción:

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

Aquí hay algunas ideas:

  • Vaya con el Enfoque # 1 y cambie el tamaño de la matriz para obtener resultados adicionales o use un tipo que se redimensione automáticamente según sea necesario (no menciona qué idioma está usando, así que no puedo ser más específico).
  • Puede ejecutar ambas declaraciones en el Método 1 dentro de una transacción para garantizar que los recuentos sean los mismos en ambas ocasiones si su base de datos lo admite.
  • No estoy seguro de lo que está haciendo con los datos, pero si es posible procesar los resultados sin almacenarlos primero, este podría ser el mejor método.

Si está realmente preocupado de que su número de filas cambie entre el recuento seleccionado y la instrucción de selección, ¿por qué no selecciona primero sus filas en una tabla temporal? De esa manera, sabes que estarás sincronizado.

¿Por qué no pones tus resultados en un vector? De esa manera, no tienes que saber el tamaño de antemano.

Es posible que desee pensar en un mejor patrón para tratar con datos de este tipo.

Ningún controlador de SQL con autoevaluación le dirá cuántas filas devolverá su consulta antes de devolver las filas, porque la respuesta podría cambiar (a menos que use una Transacción, que crea problemas por sí misma)

El número de filas no cambiará: google para ACID y SQL.

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

Solo para agregar esto porque este es el resultado principal en Google para esta pregunta. En sqlite usé esto para obtener el recuento de filas.

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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top