J'essaie de sélectionner une colonne dans une seule table (pas de jointures) et j'ai besoin du nombre de lignes, idéalement avant de commencer à récupérer les lignes. Je suis arrivé à deux approches qui fournissent les informations dont j'ai besoin.
Approche 1:
SELECT COUNT( my_table.my_col ) AS row_count
FROM my_table
WHERE my_table.foo = 'bar'
Ensuite
SELECT my_table.my_col
FROM my_table
WHERE my_table.foo = 'bar'
Ou approche 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'
Je le fais parce que mon pilote SQL (SQL Native Client 9.0) ne me permet pas d'utiliser SQLRowCount sur une instruction SELECT mais j'ai besoin de connaître le nombre de lignes dans mon résultat afin d'allouer un tableau avant de lui affecter des informations. L'utilisation d'un conteneur alloué dynamiquement n'est malheureusement pas une option dans ce domaine de mon programme.
Je crains que le scénario suivant ne se produise:
L'approche 2 interdit-elle ce problème?
De plus, l'une des deux approches sera-t-elle plus rapide? Si oui, lequel?
Enfin, existe-t-il une meilleure approche que je devrais envisager (peut-être un moyen de demander au pilote de renvoyer le nombre de lignes dans un résultat SELECT à l'aide de SQLRowCount?)
Pour ceux qui ont demandé, j'utilise Native C++ avec le pilote SQL susmentionné (fourni par Microsoft.)
Il n'y a que deux façons d'être certain à 100% que la COUNT(*)
et la requête réelle donneront des résultats cohérents:
COUNT(*)
avec la requête, comme dans votre approche 2. Je recommande le formulaire que vous montrez dans votre exemple, pas le formulaire de sous-requête corrélée montré dans le commentaire de kogus.SNAPSHOT
ou SERIALIZABLE
.L'utilisation de l'un de ces niveaux d'isolement est importante car tout autre niveau d'isolement permet aux nouvelles lignes créées par d'autres clients de devenir visibles dans votre transaction actuelle. Lisez la documentation MSDN sur SET TRANSACTION ISOLATION
pour plus de détails.
Si vous utilisez SQL Server, après votre requête, vous pouvez sélectionner la fonction @@ RowCount (ou si votre jeu de résultats peut contenir plus de 2 milliards de lignes, utilisez RowCount_Big () = fonction). Cela renverra le nombre de lignes sélectionnées par l'instruction précédente ou le nombre de lignes affectées par une instruction d'insertion/mise à jour/suppression.
SELECT my_table.my_col
FROM my_table
WHERE my_table.foo = 'bar'
SELECT @@Rowcount
Ou si vous voulez que le nombre de lignes inclus dans le résultat envoyé soit similaire à l'approche n ° 2, vous pouvez utiliser la clause OVER .
SELECT my_table.my_col,
count(*) OVER(PARTITION BY my_table.foo) AS 'Count'
FROM my_table
WHERE my_table.foo = 'bar'
L'utilisation de la clause OVER aura de bien meilleures performances que l'utilisation d'une sous-requête pour obtenir le nombre de lignes. L'utilisation de @@ RowCount aura les meilleures performances car il n'y aura aucun coût de requête pour l'instruction select @@ RowCount
Mise à jour en réponse au commentaire: L'exemple que je donne donnerait le # de lignes dans la partition - défini dans ce cas par "PARTITION BY my_table.foo". La valeur de la colonne dans chaque ligne est le # de lignes avec la même valeur de my_table.foo. Étant donné que votre exemple de requête avait la clause "WHERE my_table.foo = 'bar'", toutes les lignes de l'ensemble de résultats auront la même valeur de my_table.foo et par conséquent, la valeur dans la colonne sera la même pour toutes les lignes et égale (dans ce cas) c'est le # de lignes dans la requête.
Voici un exemple meilleur/plus simple de la façon d'inclure une colonne dans chaque ligne qui est le nombre total de lignes dans le jeu de résultats. Supprimez simplement la clause facultative Partition By.
SELECT my_table.my_col, count(*) OVER() AS 'Count'
FROM my_table
WHERE my_table.foo = 'bar'
Si vous êtes préoccupé par le nombre de lignes qui remplissent la condition peut changer dans les quelques millisecondes depuis l'exécution de la requête et la récupération des résultats, vous pouvez/devez exécuter les requêtes à l'intérieur d'une transaction:
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
Cela retournerait toujours les valeurs correctes.
De plus, si vous utilisez SQL Server, vous pouvez utiliser @@ ROWCOUNT pour obtenir le nombre de lignes affectées par la dernière instruction, et rediriger la sortie de la requête real vers une table temporaire ou une variable de table, donc vous pouvez tout renvoyer, sans avoir besoin d'une transaction:
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
L'approche 2 renverra toujours un nombre qui correspond à votre ensemble de résultats.
Je vous suggère cependant de lier la sous-requête à votre requête externe, pour garantir que la condition de votre compte correspond à la condition de l'ensemble de données.
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';
Voici quelques idées:
Si vous êtes vraiment préoccupé par le fait que votre nombre de lignes changera entre le nombre sélectionné et l'instruction select, pourquoi ne pas d'abord sélectionner vos lignes dans une table temporaire? De cette façon, vous savez que vous serez synchronisé.
IF (@@ROWCOUNT > 0)
BEGIN
SELECT my_table.my_col
FROM my_table
WHERE my_table.foo = 'bar'
END
Pourquoi ne mettez-vous pas vos résultats dans un vecteur? De cette façon, vous n'avez pas besoin de connaître la taille à l'avance.
Juste pour ajouter ceci car c'est le meilleur résultat dans google pour cette question. Dans sqlite, j'ai utilisé cela pour obtenir le nombre de lignes.
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
Vous voudrez peut-être réfléchir à un meilleur modèle pour traiter les données de ce type.
Aucun pilote SQL auto-détecté ne vous dira combien de lignes votre requête renverra avant de renvoyer les lignes, car la réponse peut changer (sauf si vous utilisez une transaction, ce qui crée ses propres problèmes).
Le nombre de lignes ne changera pas - google pour ACID et SQL.