2017-08-03 3 views
0

Ich habe drei Tabellen (Users, Items, UserItems) wobei UserItems eine Viele-zu-Viele-Beziehung zwischen Benutzern und Items ist. Was ich brauche, ist eine Abfrage, die alle Elemente erhält, die ein Benutzer nicht hat.SQL Server-Abfrage, wo keine Beziehung zwischen drei Tabellen existiert

ich eine SQL-Fiddle Beispiel mit einigen gefälschten Daten hier erstellt haben: http://sqlfiddle.com/#!6/a6856/1

CREATE TABLE Users (userID INT, firstName NVARCHAR(50)) 
CREATE TABLE Items (itemID INT, itemName NVARCHAR(50)) 
CREATE TABLE UserItems (userID INT, itemID INT) 

INSERT INTO Users (userID, firstName) 
VALUES (1, 'Jack'), (2, 'Jill'), (3, 'John'), (4, 'Jane'); 

INSERT INTO Items (itemID, itemName) 
VALUES (1, 'Bucket'), (2, 'Water'), (3, 'Cast'); 

INSERT INTO UserItems (userID, itemID) 
VALUES (1, 1), (1, 2), (1, 3), (2, 3); 

gewünschten Ergebnisse Benutzer und welche Gegenstände sie nicht haben:

--------------------------------- 
| userID | firstName | itemName | 
|--------|-----------|----------| 
| 2  | Jill  | Bucket | 
| 2  | Jill  | Water | 
| 3  | John  | Bucket | 
| 3  | John  | Water | 
| 3  | John  | Cast  | 
| 4  | Jane  | Bucket | 
| 4  | Jane  | Water | 
| 4  | Jane  | Cast  | 
--------------------------------- 

HINWEIS: Jack tut muss nicht in den Ergebnissen sein, weil er alle drei Elemente hat.

Vielen Dank!

Antwort

1
SELECT u.userId, u.firstName, i.itemId, i.itemName 
    FROM users u CROSS JOIN items i 
    LEFT OUTER JOIN useritems ui 
    ON u.userID = ui.userId AND i.itemId = ui.itemId 
    WHERE ui.itemId IS NULL 
    ORDER BY 1, 3; 
+0

Die Unterabfragen sind im Hinblick auf die Leistung teuer. Normalerweise wird ein Join schneller ausgeführt als eine Unterabfrage. Google es für "JOINS vs SUBQUERIES". Dies ist keine Faustregel, manchmal wird der Ausführungsplan gleichwertig sein. –

+0

Dieser funktioniert genauso gut wie die Antwort von @Nolan, aber ich mag diesen einen besser, weil es sauberer ist. Ich bemerke keinen Geschwindigkeitsunterschied, aber ich höre von der "Faustregel". – zabuuq

4
select * 
from Users as u inner join Items as i on 1=1 
where not exists(select 0 from UserItems as ui where ui.userID=u.userID and i.itemID=ui.itemID) 
order by userID,firstName,itemID 
 
+--------+-----------+--------+----------+ 
| userID | firstName | itemID | itemName | 
+--------+-----------+--------+----------+ 
| 2  | Jill  | 1  | Bucket | 
| 2  | Jill  | 2  | Water | 
| 3  | John  | 1  | Bucket | 
| 3  | John  | 2  | Water | 
| 3  | John  | 3  | Cast  | 
| 4  | Jane  | 1  | Bucket | 
| 4  | Jane  | 2  | Water | 
| 4  | Jane  | 3  | Cast  | 
+--------+-----------+--------+----------+ 
+0

perfekt funktioniert, danke! – zabuuq

1

Das sieht wie ein guter Anwendungsfall für den Betreiber AUSSER.

SELECT  U.UserID, I.itemID 
FROM  Users U 
CROSS JOIN Items I -- Gets a set of all users and all items 
EXCEPT 
SELECT  UI.UserID, UI.ItemID 
FROM  UserItems UI -- Removes the items that a user has. 
+0

Das funktioniert, aber es gibt mir nicht meine gewünschten Ergebnisse. Ich muss eine zweite Auswahl hinzufügen, um den FirstName und den ItemName zu erhalten, die ich brauchte. – zabuuq

0

NOT EXISTS scheint wie eine perfekte Passform hier

SELECT 
    usr.userID 
    , usr.firstName 
    , itm.itemName 
FROM 
    Users usr 
    , Items itm 
WHERE NOT EXISTS 
(
    SELECT * 
    FROM UserItems usrItm 
    WHERE 
     usrItm.itemID = itm.itemID 
     AND usrItm.userID = usr.userID 
) 
ORDER BY 
    usr.userID 
, itm.itemID 
+0

Danke. @nolan schlug dich jedoch auf die Antwort. – zabuuq