Maresa P.: Access: Having

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

  1. 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

    1. Hallo,

      vielen Dank für Euere Hilfe.
      Jetzt funktionierts!

      Gruß
      MAresa

  2. 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

    1. 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
      
      1. 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

        Nachfolgend der TestCode:

          
        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

        1. ... kurzer Nachtrag ...

          es muss natürlich nicht

            
          ALTER FUNCTION  
          
          

          sondern

            
          CREATE FUNCTION  
          
          

          sein.

          Ciao, Frank

        2. 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

          1. 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

      2. 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