web-dev-qa-db-fra.com

SQL: Retour "true" si la liste des enregistrements existe?

Un titre alternatif pourrait être: Vérifier l'existence de plusieurs lignes?

À l'aide d'une combinaison de SQL et de C #, je souhaite qu'une méthode renvoie true si tous les produits d'une liste existent dans une table. Si cela peut être fait tout en SQL, ce serait préférable. J'ai écrit une méthode qui indique s'il existe une seule variable productID à l'aide du code SQL suivant:

SELECT productID FROM Products WHERE ProductID = @productID

Si cela retourne une ligne, la méthode c # renvoie true, sinon false.

Maintenant, je me demande si j'ai une liste d'identifiants de produits (et non une liste énorme, normalement, moins de 20 ans). Comment puis-je écrire une requête qui renverra une ligne si tous les identifiants de produit existent et aucune ligne si un ou plusieurs identifiants de produits n'existent pas?

(Maybe something involving "IN" like:
SELECT * FROM Products WHERE ProductID IN ('1', '10', '100', 'ABC'))

MODIFIER:

Comment le résultat est exprimé n'est pas important pour moi. Que la requête retourne un 1 ou 0, un ensemble de résultats vide ou non vide, vrai ou faux n'a pas d'importance. Je préférerais que la réponse soit 1) facile à lire et à comprendre et 2) performante

J'envisageais de concaténer la liste des ID de produit avec le code SQL. Évidemment, cela ouvre le code à l’injection SQL (les ids du produit sont en fait varchar. Dans ce cas, la chance est mince, mais vous voulez toujours l’éviter). Donc, s’il existe un moyen de contourner ce problème, ce serait mieux. Utilisation de SQL Server 2005.

Les ID de produit sont varchar

29
User

Compte tenu de votre question mise à jour, ce sont les formes les plus simples:

Si ProductID est unique, vous voulez

SELECT COUNT(*) FROM Products WHERE ProductID IN (1, 10, 100)

et vérifiez ensuite ce résultat par rapport à 3, le nombre de produits que vous interrogez (cette dernière partie peut être effectuée en SQL, mais il peut être plus facile de le faire en C #, à moins que vous ne fassiez davantage avec SQL).

Si ProductID n'est pas unique, c'est

SELECT COUNT(DISTINCT ProductID) FROM Products WHERE ProductID IN (1, 10, 100)

Lorsque la question a été considérée comme nécessitant le renvoi de lignes lorsque toutes les variables ProductIds sont présentes et aucune autrement:

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 fera comme les autres réponses, énoncées ou implicites.

19
Mark Hurd

@ Mark Hurd, merci d'avoir signalé l'erreur. 

cela fonctionnera (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, créez une table mémoire temporaire (remplissez alors 1,10,100):

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

Sur votre code C #:

bool isAllExist = (int)(new SqlCommand(queryHere).ExecuteScalar()) == 1;

[MODIFIER]

Comment puis-je écrire une requête qui va renvoie une ligne si tous les identifiants de produit existe et pas de ligne si un ou plusieurs L'identifiant du produit n'existe pas?

En ce qui concerne, renvoyer une ligne (singulier) si toutes les lignes existent et aucune ligne à renvoyer si un ou plusieurs ID de produit n'existent 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 simplement rendre la condition plus courte:

return dt.Rows.Count > 0;
11
Michael Buen

Voici comment je le fais habituellement:

Juste remplacez votre requête par cette instruction SELECT * FROM table WHERE 1

   SELECT
      CASE WHEN EXISTS 
      (
            SELECT * FROM table WHERE 1
      )
      THEN 'TRUE'
      ELSE 'FALSE'
   END
8
Mahmoud Zalt
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]
1
Anthony Faull

Où est cette liste de produits que vous essayez de déterminer l'existence? Si cette liste existe dans une autre table, 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

Modifier:

Ensuite, faites TOUT le travail en C #. Cachez la liste actuelle 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;

Cela empêche la construction manuelle de requêtes SQL et conserve toute la logique de votre application dans l'application.

0
Josh Smeaton

Votre c # devra faire juste un peu de travail (en comptant le nombre d'identifiants transmis), mais essayez ceci:

select (select count(*) from players where productid in (1, 10, 100, 1000)) = 4

Modifier:

4 peut certainement être paramétré, de même que la liste des entiers. 

Si vous ne générez pas le code SQL à partir d'une chaîne entrée par l'utilisateur, vous n'avez pas à vous soucier des attaques. Si vous l'êtes, il vous suffit de vous assurer que vous n'obtenez que des entiers. Par exemple, si vous preniez la chaîne "1, 2, 3, 4", vous feriez quelque chose comme:

String.Join(",", input.Split(",").Select(s => Int32.Parse(s).ToString()))

Cela jettera si vous obtenez la mauvaise chose. Ensuite, définissez cela comme paramètre.

Assurez-vous également de tenir compte des cas spéciaux si items.Count == 0, car votre base de données s’étouffera si vous lui envoyez where ParameterID in ().

0
user24359
// 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)
0
Alec

Si vous avez les identifiants stockés dans une table temporaire (ce qui peut être fait avec une fonction C # ou un simple SQL), alors le problème devient simple et réalisable 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"

Cela retournera "tout existe" quand tous les produits dans #products existent dans la table cible (ProductTable) et ne retournera pas de ligne si ce qui précède n'est pas vrai.

Si vous ne souhaitez pas écrire dans une table temporaire, vous devez indiquer un paramètre pour le nombre de produits que vous essayez de rechercher et remplacer la table temporaire par un 'in'; clause donc 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"
0
TerrorAustralis

Si vous utilisez SQL Server 2008, je créerais une procédure stockée qui prend un paramètre table . La requête devrait 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é cela pour retourner une ligne, comme vous semblez avoir besoin. Il existe d'autres moyens de faire cela avec un WHERE NOT EXISTS (LEFT JOIN ici WHERE rhs IS 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
0
Cade Roux

Je sais que c'est vieux, mais je pense que cela aidera tous ceux qui viennent à la recherche de ...

SELECT CAST(COUNT(ProductID) AS bit) AS [EXISTS] FROM Products WHERE(ProductID = @ProductID)

Cela retournera TOUJOURS VRAI s'il existe et FAUX s'il ne le fait pas (par opposition à aucune ligne).

0
joels

Si la clause IN est un paramètre (soit sur SP, soit sur un SQL compilé à chaud), vous pouvez toujours le faire:

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, 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, mettez-la dans une table ou écrivez-la deux fois.

0
Joshua