12
votes

SQL-Query: Existe dans la sous-carte

J'ai deux tables tabdata et tabdataDetaDetail. Je veux toutes les iddata (pk) de la table parent (Tabdata) qui ont Seulement des lignes dans la table enfant (TabdataDetail, FK est Fidata) avec:

  • fiagctioncode = 11 seul ou
  • fiagctioncode = 11 et fiagctioncode = 34

    Toute autre combinaison n'est pas valide. Comment les obtenir?

    Qu'est-ce que j'ai essayé sans succès (lent et me donne aussi des lignes qui ont uniquement fiagctioncode 34):

     text alt
    (source: BILDER-HOCHLADEN.net )

    Merci pour votre temps.


    Modifier : Merci à tous pour leurs réponses. Maintenant, je n'ai malheureusement pas assez de temps pour vérifier lequel est le meilleur ou fonctionne du tout. J'ai marqué le premier travail en tant que réponse.

    Edit2: Je pense que la réponse marquée est vraiment la solution la plus efficace et la plus compacte.

    Edit3: La réponse de CoDesleuth est intéressante car elle ne renvoie que des lignes que d'avoir un seul code de fiagction = 11. Difficile de voir, parce que c'est seulement vrai pour 20 TabdataDetail-Rows OT de 41524189 Total-Rows qui en ont deux. Quoi qu'il en soit, cela n'était pas à 100% de ce que j'ai demandé ou plutôt de ce que je cherchais.


2 commentaires

Il n'est jamais mentionné quelles colonnes sont nécessaires dans la sortie. N'est-ce que des colonnes Tabdata ou des données de TabdataDetail sont-elles requises?


Ce n'est que la clé primaire (IDDATA) qui m'intéresse et doit être regroupée (et ordonnée) par (si nécessaire). Mais pour vérifier le résultat, il est préférable d'avoir un code de fiagction également.


6 Réponses :


6
votes
Select ...
From tabData As T1
Where Exists    (
                Select 1
                From tabDataDetail As TDD1
                Where TDD1.fiData = T1.idData
                    And TDD1.fiactionCode = 11
                )
    And Not Exists    (
                      Select 1
                      From tabDataDetail As TDD1
                      Where TDD1.fiData = T1.idData
                          And TDD1.fiactionCode Not In(11,34)
                    )
To expand on my logic, the first check (a correction) is to ensure that a row with fiActionCode = 11 exists. The second check works by first defining the set of rows we do not want. We do not want anything that is something other than fiActionCode = 11 or 34. Because that is the set of items we do not want, we search for anything that does not exist in that set.

6 commentaires

Merci. Mais cela me donne aussi les rangées qui ne disposent que de 34 comme fiacction à la garde d'enfants. Ceux-ci devraient être exclus.


@Tim - corrigé. Il suffit de recevoir un contrôle supplémentaire pour vous assurer que la fiacctioncode = 11 existe.


PK est iddata et le nom de la table est Tabdata, mais mis à part de cela, il semble fonctionner (je ne sais pas pourquoi). Je reçois des rangées de 400k de sorte que c'est difficile à vérifier.


@Tim - Un simple vérification de vérification serait de déterminer si une ligne résultante a une ligne tabdataDetail avec une valeur de codes de fiagction égale à quelque chose d'autre que 34 (nous savons qu'il doit avoir une ligne avec une valeur = 11). Il suffit d'ajouter une jointure intérieure à TabdataDetail ferait une inspection visuelle plus facile.


@Thomas: super efficace, gentil!


@THOMAS: Pourriez-vous s'il vous plaît simplement changer "Sélectionnez ... à partir d'Iddata comme T1" pour "Select Iddata de Tabdata comme T1"? Cela pourrait être déroutant pour les autres. Merci.



1
votes

EDIT: APOLS - Je vois ce que vous voulez dire avec des lignes d'enfants. Ce n'est pas particulièrement efficace. Merci également à Lieven pour les données.

SELECT idData FROM
tabData td
WHERE EXISTS 
(
    SELECT 1 
        FROM tabDataDetail tdd 
        WHERE tdd.fiData = td.idData AND fiActionCode = 11
 )
AND NOT EXISTS
(
    SELECT 1 
        FROM tabDataDetail tdd 
        WHERE tdd.fiData = td.idData AND fiActionCode <> 11
 )
UNION
SELECT idData 
    FROM tabData td
    WHERE EXISTS 
    (
        SELECT 1 
            FROM tabDataDetail tdd 
            WHERE tdd.fiData = td.idData AND fiActionCode = 11
     )
    AND EXISTS
    (
        SELECT 1 
            FROM tabDataDetail tdd 
            WHERE tdd.fiData = td.idData AND fiActionCode = 34
     )
AND NOT EXISTS
(
    SELECT 1 
        FROM tabDataDetail tdd 
        WHERE tdd.fiData = td.idData AND fiActionCode NOT IN (11, 34)
 )


0 commentaires

4
votes

raisonnement fort>

  1. JOIN EXTÉRIEUR DE GAUCHE CODE> exclut tous les IDDATA qui ont une pièce d'identité différente de 11 ou 34 li>
  2. avoir code> exclut tous les IDData que seul em> ont un 34 li>
  3. Enregistrements restants (devrait) satisfaire toutes les contraintes li> ol>

    Données de test strong> p> xxx pré>

    Query strong> p>

    SELECT  *
    FROM    @tabData d
            INNER JOIN @tabDataDetail dd ON dd.fiData = d.idData
            INNER JOIN (
              SELECT  idData
              FROM    @tabData d
                      INNER JOIN @tabDataDetail dd ON dd.fiData = d.idData
                      LEFT OUTER JOIN (
                        SELECT  fiData
                        FROM    @tabDataDetail
                        WHERE   fiActionCode NOT IN (11, 34)
                      ) exclude ON exclude.fiData = d.idData
              WHERE   exclude.fiData IS NULL                
              GROUP BY
                      idData
              HAVING  MIN(fiActionCode) = 11        
            ) include ON include.idData = d.idData
    


4 commentaires

Merci, mais j'ai plusieurs 'l'identifiant multi-parties "d.iddata" ne pouvait pas être lié. " et "nom de colonne ambigu"


Avez-vous supprimé le @? La requête fonctionne sans problèmes sur mon système.


Travaille aussi (un peu lent). Merci


@Tim, une indexation correcte peut aller un long chemin à accélérer, mais ce sera difficile (sinon impossible) de battre Thomas ' ne pas exister solution.



1
votes

édité ma réponse basée sur des éclaircissements donnés dans des commentaires sur d'autres réponses.

select td.idData
 from tabData td
  left join tabDataDetail tdd
   on td.idData = tdd.fiData
    and tdd.fiActionCode = 11
  left join tabDataDetail tdd2
   on td.idData = tdd2.fiData
    and tdd2.fiActionCode = 34
  left join tabDataDetail tdd3
   on td.idData = tdd3.fiData
    and tdd3.fiActionCode not in (11,34)
 where (tdd.fiData is not null
  or (tdd.fiData is not null and tdd2.fiData is not null))
  and tdd3.fiData is null
 group by td.idData


0 commentaires

1
votes

merci @ Lieven pour le code de données pour tester ceci: xxx pré>

requête: p>

idData
-----------
1
2

(2 row(s) affected)


9 commentaires

@Codesléuth, ne le mentionne pas. Le coût selon l'analyseur de performance est de 24% pour ma solution, 32% pour votre choix. Je suis intéressé à savoir dans un scénario du monde réel si cela tient.


Je n'ai pas vraiment examiné la question ou cette réponse dans un détail, mais comme un point général n'existe pas est plus efficace que Count (*) dans SQL Server tel quel une jointure anti-semi. Il suffit de vérifier qu'une ligne correspondante n'existe pas. Ne comptez pas tous ceux qui correspondent.


@Lieven: Coût de quoi? Juste la sous-requête? Désolé, je semble avoir obtenu pas dans mélangé dans ma tête avec n'existe pas - je me trompe, la performance est en réalité meilleure que le compte dans ma réponse. Je vais le modifier. Bravo!


@Lieven, @Martin Smith: corrigé, désolé à ce sujet :(


La requête renvoie 212040 Tabdata lignes mais le résultat correct devrait être de 212050. Je ne sais pas encore pourquoi.


@Tim: Lesquels manquent-ils?


Intéressant. Les 10 rangées ont 2 lignes de détail chacune avec deux codes de fiacction = 11 (ce qui devrait être impossible dans mon BL). Je dois donc admettre que votre résultat est le plus précis.


Il ne s'agissait pas de la question à 100% à 100% à la recherche de fiagctioncode = 11 uniquement en termes de non-autres codes de fiacction ou uniquement en termes de célibataire. J'ai besoin d'une première alternative. Mais était intéressant de voir qu'il est possible d'avoir plus d'un 11. Merci.


@IM: Cool, j'avais supposé que vous aviez une contrainte en place pour cela, de ce qu'il semblait dans votre question. Je suis content que ma réponse vous ait aidé un peu plus loin :)



1
votes

Cela le fait avec un passage à travers les données que je pense.

Cela dépend de la distribution des données, qu'il soit préférable de faire 2 recherches distinctes. P>

WITH matches AS
(
SELECT fiData
FROM tabDataDetail 
GROUP BY fiData
HAVING COUNT(CASE WHEN fiactionCode = 11 THEN 1 END) > 0
AND COUNT(CASE WHEN fiactionCode NOT IN (11,34) THEN 1 END) = 0
)
SELECT ...
FROM idData i
JOIN matches m
ON  m.fiData = i.idData


1 commentaires

Cela fonctionne aussi (lorsque vous changez 'Sélectionnez ... de IDData I' pour sélectionner IDData de Tabdata I '). Merci.