Express Union qui dépend d'autres résultats
-
16-10-2019 - |
Question
Je voudrais faire une union de deux tables qui sont en fait dans une constellation hiérarchique.
Comment puis-je écrire que l'algèbre relationnelle? Dis tableau A est le parent de B dans un rapport 1:. N relation
D'abord, je fais une sélection sur A et je veux faire seulement une union avec ces entrées B qui seraient en se joindre à la sélection sur A.
Je voudrais l'écrire la façon dont une base de données serait l'évaluer.
Y at-il une telle chose comme une union conditionnelle?
La solution
Je ne sais pas ce que vous entendez par une UNION. S'il vous plaît expliquer!
Si vous faites référence à l'opérateur UNION qui combine verticalement « jeu de lignes », alors cela pourrait faire l'affaire pour vous:
SELECT
CASE X.Which WHEN 1 THEN A.Column1 ELSE B.Column1 END Column1,
CASE X.Which WHEN 1 THEN A.Column2 ELSE B.Column2 END Column2,
...
FROM
TableA A
CROSS JOIN (
SELECT 1
UNION ALL SELECT 2
) X (Which)
LEFT JOIN TableB B
ON B.AId = A.Id
AND X.Which = 2
WHERE
X.Which = 1
OR B.AId IS NOT NULL
Cela va faire une opération de balayage unique sur les deux tables, plutôt que les au moins deux analyses sur le tableau A pour la requête suivante:
SELECT
A.Column1,
A.Column2,
...
FROM
TableA A
UNION ALL
SELECT
B.Column1,
B.Column2,
...
FROM
TableA A
INNER JOIN TableB B
ON B.AId = A.Id
Maintenant, il est possible quand vous avez dit que vous UNION voulais simplement dire une intersection mathématique, auquel cas la dernière SELECT sera au-dessus de ce que vous avez besoin, simple opération JOIN:
SELECT
A.Whatever,
B.Whatever,
...
FROM
TableA A
INNER JOIN TableB B
ON B.AId = A.Id
UPDATE
Apparemment, certains moteurs de DB ont des capacités différentes. Par exemple, les deux dernières requêtes dans mon exemple de script ci-dessous (aurait) ont des plans d'exécution très différents dans MySQL, mais ils sont identiques dans SQL Server, qui sélectionne le meilleur chemin d'accès en changeant l'ordre de jointure, la position d'entrée gauche / droite, et le déplacement Les conditions dans le au besoin. Il n'est pas coincé faire JOIN première et seconde oùS.
Pour appuyer ma demande à propos de serveur SQL, je cuisinais jusqu'à un script de test. Cette charge une table parent avec 1 million de lignes et une table enfant avec environ 2,5 millions de lignes. Les lignes individuelles que nous recherchons obtenir mis bien profondément dans la pile (totalement inutile, je sais, mais bon, c'était amusant).
CREATE DATABASE Proof;
GO
ALTER DATABASE Proof SET RECOVERY SIMPLE --no need to bloat the tran log
USE Proof;
GO
CREATE TABLE books (
id int identity(1,1) NOT NULL CONSTRAINT PK_books PRIMARY KEY CLUSTERED,
title varchar(100)
);
CREATE TABLE characters (
book_id int not null constraint fk_characters foreign key references books (id),
name varchar(100),
CONSTRAINT PK_characters PRIMARY KEY CLUSTERED (book_id, name)
);
SET NOCOUNT ON;
DECLARE
@book int,
@rowcount int,
@lastbookid int,
@which int;
SET @book = Coalesce((SELECT Count(*) FROM books), 0);
SET @which = 1;
WHILE 1 = 1 BEGIN
INSERT books
SELECT Left(Replicate('-' + Convert(varchar(11), @book + v.number), 20), 100)
FROM master.dbo.spt_values v
WHERE
v.type = 'P'
AND v.number < 1000000 - @book;
SELECT @rowcount = @@rowcount, @lastbookid = scope_identity();
IF @rowcount = 0 BREAK;
SET @book = @book + @rowcount;
INSERT characters
SELECT
B.id, Left(Replicate('|' + Convert(varchar(11), v.number), 20), 100)
FROM
books B
CROSS JOIN master.dbo.spt_values v
WHERE
B.id BETWEEN @lastbookid - @rowcount + 1 AND @lastbookid
AND v.type = 'P'
AND v.number BETWEEN 1 AND Convert(int, Rand() * 4) + 1;
IF @book >= 250000 AND @which = 1 BEGIN -- put them deep inside
INSERT books VALUES ('The Frog and the Sorcerer');
INSERT characters
SELECT scope_identity(), name
FROM (
SELECT 'Frog' UNION ALL SELECT 'Sorcerer'
) x (name);
SET @book = @book + 1;
SET @which = @which + 1;
END
ELSE IF @book >= 500000 AND @which = 2 BEGIN
INSERT books VALUES ('The Princess and the Pea');
INSERT characters
SELECT scope_identity(), name
FROM (
SELECT 'Princess' UNION ALL SELECT 'Pea'
) x (name);
SET @book = @book + 1;
SET @which = @which + 1;
END
ELSE IF @book >= 750000 AND @which = 3 BEGIN
INSERT books VALUES ('Two Ways to Tango');
INSERT characters
SELECT scope_identity(), name
FROM (
SELECT 'Tango Alpha' UNION ALL SELECT 'Tango Omega'
) x (name);
SET @book = @book + 1;
SET @which = @which + 1;
END;
END;
GO
SET SHOWPLAN_ALL ON;
GO
SELECT A.title,B.name
FROM
books A
LEFT JOIN characters B
ON A.id = B.book_id
WHERE
A.title IN ('Two Ways to Tango', 'The Frog and the Sorcerer')
OPTION (MAXDOP 1);
GO
SET SHOWPLAN_ALL OFF;
GO
SET SHOWPLAN_ALL ON;
GO
SELECT A.title, B.name
FROM
(
SELECT id, title FROM books A
WHERE title IN ('Two Ways to Tango', 'The Frog and the Sorcerer')
) A
LEFT JOIN characters B
ON A.id = B.book_id
OPTION (MAXDOP 1);
GO
SET SHOWPLAN_ALL OFF;
GO
USE master;
GO
DROP DATABASE Proof;
Les deux plans d'exécution sont identiques. Je retins le parallélisme car il était le bruit inutile (les plans étaient toujours les mêmes). Voici le résultat de la SHOWPLAN avec la requête retirée (la seule partie qui était différente).
StmtText StmtId NodeId Parent PhysicalOp LogicalOp Argument DefinedValues EstimateRows EstimateIO EstimateCPU AvgRowSize TotalSubtreeCost OutputList Warnings Type Parallel EstimateExecutions
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ------ ------ ------ -------------------- -------------------- ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------- ------------ ---------- ------------ ---------- ---------------- ----------------------- -------- -------- -------- ------------------
1 1 0 NULL NULL 1 NULL 2.994377 NULL NULL NULL 12.71991 NULL NULL SELECT 0 1
|--Nested Loops(Left Outer Join, OUTER REFERENCES:([A].[id])) 1 2 1 Nested Loops Left Outer Join OUTER REFERENCES:([A].[id]) NULL 2.994377 0 0.0000125165 151 12.71991 [A].[title], [B].[name] NULL PLAN_ROW 0 1
|--Clustered Index Scan(OBJECT:([Proof].[dbo].[books].[PK_books] AS [A]), WHERE:([Proof].[dbo].[books].[title] as [A].[title]='The Frog and the Sorcerer' OR [Proof].[dbo].[books].[title] as [A].[title]='Two Ways to Tango')) 1 3 2 Clustered Index Scan Clustered Index Scan OBJECT:([Proof].[dbo].[books].[PK_books] AS [A]), WHERE:([Proof].[dbo].[books].[title] as [A].[title]='The Frog and the Sorcerer' OR [Proof].[dbo].[books].[title] as [A].[title]='Two Ways to Tango') [A].[id], [A].[title] 1 10.73646 1.100157 114 11.83662 [A].[id], [A].[title] NULL PLAN_ROW 0 1
|--Clustered Index Seek(OBJECT:([Proof].[dbo].[characters].[PK_characters] AS [B]), SEEK:([B].[book_id]=[Proof].[dbo].[books].[id] as [A].[id]) ORDERED FORWARD) 1 4 2 Clustered Index Seek Clustered Index Seek OBJECT:([Proof].[dbo].[characters].[PK_characters] AS [B]), SEEK:([B].[book_id]=[Proof].[dbo].[books].[id] as [A].[id]) ORDERED FORWARD [B].[name] 2.994377 0.003125 0.0001602938 50 0.003285294 [B].[name] NULL PLAN_ROW 0 0
Autres conseils
Si je comprends bien la question, votre tâche peut être résolu en utilisant des requêtes récursives. Oracle (voir et SQLServer (2005 et plus) à la fois soutenir. Certes, différents fournisseurs de SGTP utilisent une syntaxe légèrement différente.
Vous Intersection moyen? Tenez compte relvars Customers
et Orders
avec une correspondance où un client a zéro, un ou plusieurs ordres (je ne parlerais à cela comme une hiérarchie, cependant). Pour trouver des clients qui ont des commandes:
( Customers { customer_ID } ) INTERSECT ( Orders { customer_ID } )
Je soupçonne plutôt ce n'est pas ce que vous entendez, dans lequel il vous plaît modifier votre question d'ajouter par exemple les données et les résultats attendus.