web-dev-qa-db-fra.com

Erreur - "L'opérateur UNION doit avoir un nombre égal d'expressions" lors de l'utilisation de CTE pour la sélection récursive

En ce moment, j'ai une table tblLocation avec les colonnes ID, Location, PartOfID.

La table est connectée de manière récursive à elle-même: PartOfID -> ID

Mon but est d'avoir une sortie sélectionnée comme suit:

> France > Paris > AnyCity >

Explication: AnyCity est situé à Paris, Paris est situé en France.

Ma solution que j'ai trouvée jusqu'à présent était la suivante:

; with q as (
select ID,Location,PartOf_LOC_id from tblLocatie t
where t.ID = 1 -- 1 represents an example
union all
select t.Location + '>' from tblLocation t
inner join q parent on parent.ID = t.LOC_PartOf_ID
)
select * from q

Malheureusement, j'obtiens l'erreur suivante:

Toutes les requêtes combinées à l'aide d'un opérateur UNION, INTERSECT ou EXCEPT doivent avoir un nombre égal d'expressions dans leurs listes de cibles.

Si vous avez une idée de la façon dont je pourrais corriger ma sortie, ce serait génial.

12
user2871811

Le problème réside ici:

--This result set has 3 columns
select LOC_id,LOC_locatie,LOC_deelVan_LOC_id from tblLocatie t
where t.LOC_id = 1 -- 1 represents an example

union all

--This result set has 1 columns   
select t.LOC_locatie + '>' from tblLocatie t
inner join q parent on parent.LOC_id = t.LOC_deelVan_LOC_id

Pour utiliser union ou union all nombre de colonnes et leurs types doivent être identiques, traversez tous les ensembles de résultats.

Je suppose que vous devriez simplement ajouter la colonne LOC_deelVan_LOC_id à votre deuxième jeu de résultats

16
Yosi Dahari

Le second result set n'a qu'une colonne, mais il devrait avoir 3 colonnes pour qu'il soit contenu dans le premier result set 

(les colonnes doivent correspondre lorsque vous utilisez UNION)

Essayez d’ajouter ID en tant que première colonne et PartOf_LOC_id à votre result set afin de pouvoir effectuer la UNION.

;
WITH    q AS ( SELECT   ID ,
                    Location ,
                    PartOf_LOC_id
           FROM     tblLocation t
           WHERE    t.ID = 1 -- 1 represents an example
           UNION ALL
           SELECT   t.ID ,
                    parent.Location + '>' + t.Location ,
                    t.PartOf_LOC_id
           FROM     tblLocation t
                    INNER JOIN q parent ON parent.ID = t.LOC_PartOf_ID
         )
SELECT  *
FROM    q
3
ARA

Ensuite, le nombre de colonnes doit correspondre entre les deux parties de l'union.

Afin de construire le chemin complet, vous devez "agréger" toutes les valeurs de la colonne Location. Vous devez toujours sélectionner l'id et les autres colonnes à l'intérieur du CTE pour pouvoir vous joindre correctement. Vous vous en débarrassez simplement en ne les sélectionnant pas dans la sélection externe:

with q as 
(
   select ID, PartOf_LOC_id, Location, ' > ' + Location as path
   from tblLocation 
   where ID = 1 

   union all

   select child.ID, child.PartOf_LOC_id, Location, parent.path + ' > ' + child.Location 
   from tblLocation child
     join q parent on parent.ID = t.LOC_PartOf_ID
)
select path
from q;
3

Bien que ce soit un ancien post, je partage un autre exemple de travail.

"LE COMPTE DE COLONNE AINSI QUE CHAQUE COLONNE DE DONNEES DE DONNEES DOIT ÊTRE CORRESPONDANT LORSQUE 'UNION' OR 'UNION ALL' IS UTILISE"

Prenons un exemple:

1:

En SQL si nous écrivons - SELECT 'colonne1', 'colonne2' (NOTE: n'oubliez pas de spécifier les noms entre guillemets) Dans un jeu de résultats, des colonnes vides avec deux en-têtes seront affichées: colonne1 et colonne2.

2: Je partage un cas simple que j'ai rencontré.

J'ai eu sept colonnes avec peu de types de données différents en SQL. C'est à dire. identifiant unique, date/heure, nvarchar

Ma tâche était de récupérer un ensemble de résultats séparés par des virgules avec un en-tête de colonne. Ainsi, lorsque j'exporte les données au format CSV, j'ai des lignes séparées par des virgules avec la première ligne comme en-tête et des noms de colonne respectifs.

SELECT CONVERT(NVARCHAR(36), 'Event ID') + ', ' + 
'Last Name' + ', ' + 
'First Name' + ', ' + 
'Middle Name' + ', ' + 
CONVERT(NVARCHAR(36), 'Document Type') + ', ' + 
'Event Type' + ', ' + 
CONVERT(VARCHAR(23), 'Last Updated', 126)

UNION ALL

SELECT CONVERT(NVARCHAR(36), inspectionid) + ', ' + 
       individuallastname + ', ' + 
       individualfirstname + ', ' + 
       individualmiddlename + ', ' +
       CONVERT(NVARCHAR(36), documenttype) + ', ' + 
       'I' + ', ' +
       CONVERT(VARCHAR(23), modifiedon, 126)
FROM Inspection

Ci-dessus, les colonnes 'inspectionid' & 'documenttype' ont le type de données uniqueidentifer et sont donc appliquées à CONVERT(NVARCHAR(36)). La colonne 'modifiedon' est datetime et a donc été appliquée CONVERT(NVARCHAR(23), 'modifiedon', 126).

La seconde requête SELECT correspondante correspond à la première requête SELECT selon le type de données de chaque colonne.

0
Binoy

Vous pouvez utiliser une fonction scalaire récursive: -

set nocount on

create table location (
    id int,
    name varchar(50),
    parent int
)
insert into location values
    (1,'france',null),
    (2,'paris',1),
    (3,'belleville',2),
    (4,'lyon',1),
    (5,'vaise',4),
    (6,'united kingdom',null),
    (7,'england',6),
    (8,'manchester',7),
    (9,'fallowfield',8),
    (10,'withington',8)
go
create function dbo.breadcrumb(@child int)
returns varchar(1024)
as begin
    declare @returnValue varchar(1024)=''
    declare @parent int
    select @returnValue+=' > '+name,@parent=parent
    from location
    where id=@child
    if @parent is not null
        set @returnValue=dbo.breadcrumb(@parent)+@returnValue
    return @returnValue
end
go

declare @location int=1
while @location<=10 begin
    print dbo.breadcrumb(@location)+' >'
    set @location+=1
end

produit: -

 > france >
 > france > paris >
 > france > paris > belleville >
 > france > lyon >
 > france > lyon > vaise >
 > united kingdom >
 > united kingdom > england >
 > united kingdom > england > manchester >
 > united kingdom > england > manchester > fallowfield >
 > united kingdom > england > manchester > withington >
0
dav1dsm1th