web-dev-qa-db-fra.com

Relation parent-enfant dans la même table

J'ai une table stockant les enregistrements parent/enfant en tant que tels:

+-------+------------+---------+---------+------------+-----------+
|custid | custname   | deptid  | company |parentcustid| enrolled  |
+=======+============+=========+=========+============+===========+
| 7060  | Sally      |   AB1   | comp1   |  null      |     1     |
| 6953  | Ajit       |   AB7   | comp2   |  7060      |     1     |
| 6957  | Rahul      |   DE1   | comp3   |  7060      |     1     |
| 6958  | uday       |   TG6   | comp4   |  7060      |     1     |
| 6959  | john       |   HY7   | comp5   |  7060      |     1     |
| 6960  | netaji     |   HY5   | comp6   |  7060      |     1     |
| 6961  | prakriti   |   GT6   | comp7   |  7060      |     1     |
| 6962  | sachin     |   KL7   | comp8   |  7060      |     0     |
| 6963  | santosh    |   KK5   | comp9   |  7060      |     1     |
| 6964  | Ravi       |   PP0   | comp10  |  7060      |     1     |
+-------+------------+---------+---------+------------+-----------+

Est-il possible de renvoyer des enregistrements où l'enregistrement parent est inscrit et l'enregistrement enfant associé n'est pas inscrit?

Cela renvoie ce dont j'ai besoin pour 1 client spécifique:

select a.custid, a.custname, a.deptid, a.company, a.parentcustid, a.enrolled
from customer a
where a.company = 'comp1' and a.Enrolled = 1
union all
select a.custid, a.custname, a.deptid, a.company, a.parentcustid, a.enrolled
from customer a
where a.parentcustid= 7060 and b.Enrolled = 0

+-------+------------+---------+---------+------------+-----------+
|custid | custname   | deptid  | company |parentcustid| enrolled  |
+=======+============+=========+=========+============+===========+
| 7060  | Sally      |   AB1   | comp1   |  null      |     1     |
| 6962  | sachin     |   KL7   | comp8   |  7060      |     0     |
+-------+------------+---------+---------+------------+-----------+

Comment puis-je structurer la requête pour renvoyer ce type d'ensemble de résultats pour tous les enregistrements enfants parents dans la table?

5
obautista

Si vous n'avez qu'un seul niveau d'enfants, vous pouvez rejoindre les tables

SELECT
    a.custid, a.custname, a.deptid, a.company,
    b.custid AS bcustid, b.custname AS bcustname, b.deptid AS bdeptid, b.company AS bcompany
FROM
    customer a
    LEFT JOIN customer b
        ON a.custid = b.parentcustid AND b.Enrolled = 0
WHERE
    a.parentcustid  IS NULL AND a.Enrolled = 1

De cette façon, vous ne perdez pas la relation entre le parent et le dossier enfant.

Étant donné que vous savez que les parents sont inscrits et les enfants non, le fait de conserver la colonne enrolled n'ajoute aucune nouvelle information.


Si vous souhaitez conserver la forme d'origine de votre tableau de résultats, utilisez une colonne supplémentaire pour le tri

SELECT
    custid, custname, deptid, company, parentcustid, enrolled,
    10 * custid AS orderKey
FROM customer
WHERE parentcustid IS NULL AND Enrolled = 1
UNION ALL
SELECT
    custid, custname, deptid, company, parentcustid, enrolled,
    10 * parentcustid + 1 AS orderKey
FROM customer
WHERE parentcustid IS NOT NULL AND Enrolled = 0
ORDER BY orderKey, custid

Notez que cet ORDER BY est appliqué à l'ensemble de l'union, pas seulement au deuxième SELECT. Ainsi, les enfants sont toujours répertoriés juste en dessous de leurs parents.

Au fait, multiplier la clé par 2 suffit pour obtenir l'effet souhaité pour la clé de commande. Il s'affiche simplement mieux si vous multipliez par 10. Si vos identifiants client comportent toujours un maximum de 4 chiffres, vous pouvez également utiliser 10000 * custid pour les parents et 10000 * parentcustid + custid pour les enfants et commandez uniquement par orderKey pour obtenir des enregistrements bien ordonnés.

4

Une façon courante de le faire dans SQL Server est d'utiliser un CTE récursif :

Voici vos données de test en encart:

CREATE TABLE #yourmom
(
    custid INT,
    custname VARCHAR(10),
    deptid VARCHAR(3),
    company VARCHAR(10),
    parentcustid INT,
    enrolled BIT
)

INSERT #yourmom ( custid, custname, deptid, company, parentcustid, enrolled )
SELECT x.custid, x.custname, x.deptid, x.company, x.parentcustid, x.enrolled
FROM   ( VALUES ( 7060, 'Sally   ', 'AB1', 'comp1 ', NULL, 1 ),
                ( 6953, 'Ajit    ', 'AB7', 'comp2 ', 7060, 1 ),
                ( 6957, 'Rahul   ', 'DE1', 'comp3 ', 7060, 1 ),
                ( 6958, 'uday    ', 'TG6', 'comp4 ', 7060, 1 ),
                ( 6959, 'john    ', 'HY7', 'comp5 ', 7060, 1 ),
                ( 6960, 'netaji  ', 'HY5', 'comp6 ', 7060, 1 ),
                ( 6961, 'prakriti', 'GT6', 'comp7 ', 7060, 1 ),
                ( 6962, 'sachin  ', 'KL7', 'comp8 ', 7060, 0 ),
                ( 6963, 'santosh ', 'KK5', 'comp9 ', 7060, 1 ),
                ( 6964, 'Ravi    ', 'PP0', 'comp10', 7060, 1 )
        ) AS x ( custid, custname, deptid, company, parentcustid, enrolled );

Et voici comment un CTE récursif le rechercherait:

WITH yourdad
AS ( SELECT y.custid, y.custname, y.deptid, y.company, y.parentcustid, y.enrolled
     FROM   #yourmom AS y
     WHERE  y.parentcustid IS NULL
            AND y.enrolled = 1
     UNION ALL
     SELECT ym2.custid, ym2.custname, ym2.deptid, ym2.company, ym2.parentcustid, ym2.enrolled
     FROM   yourdad AS yd
     JOIN   #yourmom AS ym2
     ON ym2.parentcustid = yd.custid
        AND ym2.enrolled = 0 )
SELECT *
FROM   yourdad;

Il y a quelques Great Posts® écrits à leur sujet:

J'espère que cela t'aides!

6
Erik Darling