SQL: retourner "true" si la liste des enregistrements existe?
Un autre titre pourrait être: Vérifier l'existence de plusieurs lignes?
en utilisant une combinaison de SQL et C# je veux une méthode pour retourner true si tous les produits dans une liste existent dans une table. Si cela peut être fait tout en SQL qui serait préférable. J'ai écrit une méthode qui retourne si un seul productID
existe en utilisant le SQL suivant:
SELECT productID FROM Products WHERE ProductID = @productID
si cela renvoie une ligne, alors la méthode c# renvoie true, false sinon.
maintenant je me demande si je avoir une liste de produits IDs (pas une liste énorme Esprit vous, normalement moins de 20). Comment puis-je écrire une requête qui retournera une rangée si toutes les id de produit existent et aucune rangée si un ou plusieurs id de produit n'existe pas?
(Maybe something involving "IN" like:
SELECT * FROM Products WHERE ProductID IN ('1', '10', '100', 'ABC'))
EDIT:
Comment le résultat est exprimé n'est pas important pour moi. Si la requête retourne un 1
ou 0
, un jeu de résultats vide ou non vide, vrai ou faux n'a pas d'importance. Je préfère la réponse est 1) facile à lire et à comprendre et 2) performant
j'envisageais de concaténer la liste des identificateurs de produit avec le SQL. Évidemment cela ouvre le code JUSQU'à L'injection SQL (les id du produit sont en fait varchar
. dans ce cas, le risque est mince, mais veulent éviter cette possibilité). Donc, si il ya un moyen de contourner ce qui serait le mieux. Utilisation de SQL Server 2005.
Produit ID varchar
12 réponses
compte tenu de votre question mise à jour, ce sont les formes les plus simples:
Si ProductID
est unique, que vous voulez
SELECT COUNT(*) FROM Products WHERE ProductID IN (1, 10, 100)
puis vérifiez ce résultat par 3
, le nombre de produits que vous demandez (cette dernière partie peut être faite en SQL, mais il peut être plus facile de le faire en C# à moins que vous ne fassiez encore plus en SQL).
Si ProductID
n'est pas unique, c'est
SELECT COUNT(DISTINCT ProductID) FROM Products WHERE ProductID IN (1, 10, 100)
quand la question a été pensée pour exiger le retour des lignes quand tous les ProductIds
sont présents et nul sinon:
SELECT ProductId FROM Products WHERE ProductID IN (1, 10, 100) AND ((SELECT COUNT(*) FROM Products WHERE ProductID IN (1, 10, 100))=3)
ou
SELECT ProductId FROM Products WHERE ProductID IN (1, 10, 100) AND ((SELECT COUNT(DISTINCT ProductID) FROM Products WHERE ProductID IN (1, 10, 100))=3)
si vous avez réellement l'intention de faire quelque chose avec les résultats. Sinon le simple SELECT 1 WHERE (SELECT ...)=3
va faire comme d'autres réponses ont exprimés ou implicites.
@Mark Hurd, merci de signaler l'erreur.
cela fonctionne (si vous utilisez Postgresql, Sql Server 2008):
create table products
(
product_id int not null
);
insert into products values(1),(2),(10),(100);
SELECT
CASE
WHEN EXISTS(
SELECT 1
FROM (values(1),(10),(100)) as x(id)
WHERE x.id NOT IN (select product_id from products))
THEN 0 --'NOT ALL'
ELSE 1 -- 'ALL'
END
si vous utilisez MySQL, faites une table mémoire temporaire (puis peuplez 1,10,100 ici):
create table product_memory(product_id int) engine=MEMORY;
insert into product_memory values(1),(10),(100);
SELECT
CASE
WHEN EXISTS(
SELECT 1
FROM product_memory
WHERE product_memory.id NOT IN (select product_id from products))
THEN 0 -- 'NOT ALL'
ELSE 1 -- 'ALL'
END
dans votre code C#:
bool isAllExist = (int)(new SqlCommand(queryHere).ExecuteScalar()) == 1;
[EDIT]
Comment puis-je écrire une requête qui va retourner une ligne si tous les id du produit existent et pas de ligne si un ou plusieurs produit id n'existe pas?
concernant, retourner une ligne (au singulier) si toutes les lignes existent, et aucune ligne pour être retournée si un ou plusieurs id de produit n'existe pas:
MySql:
SELECT 1
WHERE
NOT EXISTS(
SELECT 1
FROM product_memory
WHERE product_memory.id NOT IN (select product_id from products) )
Posgresql, Sql Server 2008:
SELECT 1
WHERE
NOT EXISTS(
SELECT 1
FROM (values(1),(10),(100)) as x(id)
WHERE x.id NOT IN (select product_id from products) )
Puis sur votre code C#:
var da = new SqlDataAdapter(queryhere, connectionhere);
var dt = new DataTable();
da.Fill(dt);
if (dt.Rows.Count > 0)
return true;
else
return false;
Ou tout simplement rendre l'état plus court:
return dt.Rows.Count > 0;
Voici comment j'ai l'habitude de faire:
remplacer votre requête avec cette déclaration SELECT * FROM table WHERE 1
SELECT
CASE WHEN EXISTS
(
SELECT * FROM table WHERE 1
)
THEN 'TRUE'
ELSE 'FALSE'
END
DECLARE @values TABLE (ProductId int)
INSERT @values (1)
INSERT @values (10)
INSERT @values (100)
SELECT CASE WHEN (SELECT COUNT(*) FROM @values v) =
(SELECT COUNT(*) FROM Products p WHERE p.ProductId IN
(SELECT v.ProductId FROM @values v))
THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END [AreAllFound]
// not familiar with C#, but C#'s equivalent of PHP's:
$count = count($productIds); // where $productIds is the array you also use in IN (...)
SELECT IF ((SELECT COUNT(*) FROM Products WHERE ProductID IN (1, 10, 100)) = $count, 1, 0)
si la clause IN est un paramètre (SP ou SQL construit à chaud), alors cela peut toujours être fait:
SELECT (SELECT COUNT(1)
FROM product_a
WHERE product_id IN (1, 8, 100)
) = (number of commas in product_id as constant)
si la clause IN est une table, alors cela peut toujours être fait:
SELECT (SELECT COUNT(*)
FROM product_a
WHERE product_id IN (SELECT Products
FROM #WorkTable)
) = (SELECT COUNT(*)
FROM #WorkTable)
si la clause IN est complexe, alors soit la bobiner dans un tableau, soit l'écrire deux fois.
si vous avez les ID stockés dans une table temp (qui peut être fait par une certaine fonction C# ou SQL simple) alors le problème devient facile et faisable en SQL.
select "all exist"
where (select case when count(distinct t.id) = (select count(distinct id) from #products) then "true" else "false" end
from ProductTable t, #products p
where t.id = p.id) = "true"
ceci retournera "tout existe" quand tous les produits dans #products
existent dans la table cible (ProductTable
) et ne retournera pas une ligne si ce qui précède n'est pas vrai.
si vous n'êtes pas prêt à écrire à une table temporaire, alors vous devez ajouter un paramètre pour le nombre de produits que vous tentez de trouver, et de remplacer le temp de la table avec un""; clause de sorte que la sous-requête ressemble à ceci:
SELECT "All Exist"
WHERE(
SELECT case when count(distinct t.id) = @ProductCount then "true" else "false"
FROM ProductTable t
WHERE t.id in (1,100,10,20) -- example IDs
) = "true"
si vous utilisez SQL Server 2008, je créerais une procédure stockée qui prend un paramètre table. La requête doit alors être d'une forme particulièrement simple:
CREATE PROCEDURE usp_CheckAll
(@param dbo.ProductTableType READONLY)
AS
BEGIN
SELECT CAST(1 AS bit) AS Result
WHERE (SELECT COUNT(DISTINCT ProductID) FROM @param)
= (SELECT COUNT(DISTINCT p.ProductID) FROM @param AS p
INNER JOIN Products
ON p.ProductID = Products.ProductID)
END
j'ai changé ceci pour retourner une rangée, comme vous semblez l'exiger. Il y a d'autres façons de le faire avec un "où N'existe pas" (rejoignez ici à gauche où rhs est NULL):
CREATE PROCEDURE usp_CheckAll
(@param dbo.ProductTableType READONLY)
AS
BEGIN
SELECT CAST(1 AS bit) AS Result
WHERE NOT EXISTS (
SELECT * FROM @param AS p
LEFT JOIN Products
ON p.ProductID = Products.ProductID
WHERE Products.ProductID IS NULL
)
END
de Votre c# vous avez à faire, juste un peu de travail (en comptant le nombre d'Identifiants passés), mais essayez ceci:
select (select count(*) from players where productid in (1, 10, 100, 1000)) = 4
Edit:
4 peut certainement être paramétré, tout comme la liste des entiers.
si vous n'êtes pas en train de générer le SQL à partir de la chaîne de caractères entrée par l'utilisateur, vous n'avez pas à vous soucier des attaques. Si vous l'êtes, vous devez juste vous assurer que vous obtenez seulement des entiers. Par exemple, si vous preniez la corde "1, 2, 3, 4", Tu ferais quelque chose. comme
String.Join(",", input.Split(",").Select(s => Int32.Parse(s).ToString()))
qui jettera si vous obtenez la mauvaise chose. Alors mettez ça comme paramètre.
aussi, assurez-vous d'être sûr de cas particulier si les éléments.Count = = 0, puisque votre DB s'étouffe si vous l'envoyez where ParameterID in ()
.
Où est cette liste de produits que vous essayez de déterminer l'existence de? Si cette liste existe dans un autre tableau, vous pouvez le faire
declare @are_equal bit
declare @products int
SELECT @products =
count(pl.id)
FROM ProductList pl
JOIN Products p
ON pl.productId = p.productId
select @are_equal = @products == select count(id) from ProductList
Edit:
puis faire tout le travail en C#. Cache la liste réelle des produits dans votre application quelque part, et faites une requête LINQ.
var compareProducts = new List<Product>(){p1,p2,p3,p4,p5};
var found = From p in GetAllProducts()
Join cp in compareProducts on cp.Id equals p.Id
select p;
return compareProducts.Count == found.Count;
ceci empêche la construction de requêtes SQL à la main, et garde toute la logique de votre application dans l'application.
en supposant que vous utilisez SQL Server, le type booléen n'existe pas, mais le type bit existe, qui ne peut contenir que 0 ou 1 où 0 représente False, et 1 représente True.
je voudrais aller de cette façon:
select 1
from Products
where ProductId IN (1, 10, 100)
Ici, une valeur null ou n'ligne sera retournée (si aucune ligne n'existe).
Ou encore:
select case when EXISTS (
select 1
from Products
where ProductId IN (1, 10, 100)
) then 1 else 0 end as [ProductExists]
Ici, l'une des valeurs scalaires 1 ou 0 sera toujours retourné (si aucune ligne n'existe).
C'est peut-être trop simple, mais j'utilise toujours:
SELECT COUNT(*)>0 FROM `table` WHERE condition;