Access: Having
Maresa P.
- datenbank
Hallo liebes Forum,
ich habe folgendes Problem:
In einer Acces Datenbank habe ich zwei Tabellen:
user
------
username [text]
und
alleuser
------
username [text]
Ich möchte mir nun aus der Tabelle 'alleuser' alle 'username' ausgeben lassen, die nicht in der Tabelle user vorkommen.
Ich habe es folgendermaßen versucht:
SELECT alleuser.username
FROM alleuser INNER JOIN [user] ON alleuser.username = user.username
HAVING ((([user]![username])<>[alleuser]![username]));
Hier bekomme ich aber immer die Fehlermeldung 'Having Klausel ohne Gruppierung der Aggregatsfunktion'.
Wie funktioniert soetwas?
Vielen Dank für Euere Mühe
Maresa
Hallo Maresa
Ich möchte mir nun aus der Tabelle 'alleuser' alle 'username' ausgeben lassen, die nicht in der Tabelle user vorkommen.
Ich habe es folgendermaßen versucht:
SELECT alleuser.username
FROM alleuser INNER JOIN [user] ON alleuser.username = user.username
Für Dein Problem ist der LEFT OUTER JOIN, nicht der INNER JOIN zuständig :-)
HAVING ((([user]![username])<>[alleuser]![username]));
Ersetze die HAVING-Klausel, die hier nicht zulässig ist, durch eine WHERE-Klausel.
Verwende als Kriterium IS NULL.
Freundliche Grüße
Vinzenz
Hallo,
vielen Dank für Euere Hilfe.
Jetzt funktionierts!
Gruß
MAresa
Hi,
"HAVING" kann nur in Zusammenhang mit der Aggregatsfunktion "GROUP BY" verwendet werden und wird erst zuletzt auf das Resultset angewendet.
Wie wär's mit folgendem
SELECT [alleuser].[username]
FROM [alleuser]
WHERE [alleuser].[username] NOT IN
(SELECT [user].[username] FROM [user])
Grüße,
Frank
Hallo Frank
Wie wär's mit folgendem
SELECT [alleuser].[username]
FROM [alleuser]
WHERE [alleuser].[username] NOT IN
(SELECT [user].[username] FROM [user])
Sowas hört sich immer schrecklich unperformat an (ich weiß nicht, ob dies auch tatsächlich der Fall ist). Das sieht stets so aus, als würde jeder Datensatz aus alleuser mit vielen Datensätzen aus user verglichen wird. Keine Ahnung, was der Optimierer daraus macht.
Für mich ist sowas stets ein LEFT OUTER JOIN in Kombination mit der Bedingung IS NULL.
Freundliche Grüße
Vinzenz
Hi,
in Bezug auf Access kann ich dir das leider nicht sagen, was SQL Server betrifft, sind die Ausführungspläne und damit die Kosten ziemlich ähnlich. Beide verwenden ein Hash-Match, die Outer-Join Variante mit der doppelten verarbeiteten Zeilenmenge plus einem zusätzlichen Filter-Schritt.
Ausgabe vom Trace:
SELECT userName FROM allUsers WHERE allUsers.userName
NOT IN (SELECT userName FROM Users)
SQL:StmtCompleted 47 16 91 0
SELECT allUsers.userName FROM allUsers
LEFT OUTER JOIN Users ON allUsers.userName = Users.userName
WHERE Users.userName IS NULL
SQL:StmtCompleted 63 47 91 0
1. Spalte = Statement Duration
2. Spalte = CPU
3./4. Spalte = Logical Reads / Writes
CREATE DATABASE userTest
GO
USE userTest
GO
CREATE TABLE allUsers (
userId int NOT NULL IDENTITY(0,1) PRIMARY KEY,
userName varchar(50) NOT NULL,
)
GO
CREATE TABLE Users (
userId int NOT NULL IDENTITY(0,1) PRIMARY KEY,
userName varchar(50) NOT NULL,
)
GO
ALTER FUNCTION getUserName
(
@intValue int
)
RETURNS varchar(50)
AS BEGIN
DECLARE @returnName varchar(50)
SET @returnName = ''
WHILE @intValue <> 0 BEGIN
DECLARE @rest int
SET @rest = @intValue % 14
IF @rest < 10
SET @returnName = CHAR(48+@rest) + @returnName -- add a number
ELSE IF @rest < 14
SET @returnName = CHAR(65+(@rest-10)) + @returnName -- add a letter
SET @intValue = @intValue/14
END
RETURN @returnName
END
GO
SELECT dbo.getUserName(156554645)
DECLARE @counter int
SET @counter = 2990000
WHILE (@counter < 3000000) BEGIN
DECLARE @myName varchar(50)
SET @myName = dbo.getUserName(@counter)
INSERT INTO allUsers (userName) VALUES (@myName)
IF (@counter < 2995000) BEGIN
INSERT INTO Users (userName) VALUES (@myName)
END
SET @counter = @counter + 1
END
SELECT userName FROM allUsers
WHERE allUsers.userName
NOT IN (SELECT userName FROM Users)
GO
SELECT allUsers.userName
FROM allUsers
LEFT OUTER JOIN Users ON allUsers.userName = Users.userName
WHERE Users.userName IS NULL
GO
Viel Spass beim Nachstellen.
Ciao, Frank
... kurzer Nachtrag ...
es muss natürlich nicht
ALTER FUNCTION
sondern
CREATE FUNCTION
sein.
Ciao, Frank
Hallo Frank
in Bezug auf Access kann ich dir das leider nicht sagen, was SQL Server betrifft, sind die Ausführungspläne und damit die Kosten ziemlich ähnlich. Beide verwenden ein Hash-Match, die Outer-Join Variante mit der doppelten verarbeiteten Zeilenmenge plus einem zusätzlichen Filter-Schritt.
Ein interessantes Ergebnis, danke!
Hätte ich nicht gedacht. Aber denken und überprüfen sind zwei Paar Schuhe.
Freundliche Grüße
Vinzenz
N'Abend!
Ja, mich hatte es auch in dem Moment interessiert und mir war grad etwas langweilig :)
Aber ob MS Access _dies_ genauso sieht ... keine Ahnung, da kann ich mir keine Ausführungspläne anzeigen lassen.
Ciao und gut Nacht :)
Frank
yo,
Sowas hört sich immer schrecklich unperformat an (ich weiß nicht, ob dies auch tatsächlich der Fall ist). Das sieht stets so aus, als würde jeder Datensatz aus alleuser mit vielen Datensätzen aus user verglichen wird. Keine Ahnung, was der Optimierer daraus macht.
das sollte bei einem guten dbms eigentlich nicht passieren. die unterabfrage müsste nur einmal ausgeführt werden und das war es dann, da sie unabhängig von der oberen abfrage ist.
Ilja