2016-05-17 16 views
0

Ich habe Probleme, etwas zu erreichen, das scheint, als sollte es einfach sein. Das folgende Beispiel zeigt die Menschen in Klassen eingeschrieben sind, und eine Person kann in mehreren Klassen eingeschrieben werden, aber nur einer zur Zeit:SELECT Werte aus Tabelle mit gruppierten ID aber Unterschiede in nicht gruppierten Spalten

DECLARE @Test TABLE 
(
    PersonId int NOT NULL, 
    LocationId int NOT NULL, 
    ClassId int NOT NULL, 
    IsEnrolled bit NOT NULL, 
    IsExited bit NOT NULL 
) 

Daten hinzufügen:

INSERT INTO @Test 
     SELECT 1,5,6,1,0 
UNION SELECT 1,6,7,0,1 
UNION SELECT 2,5,8,1,0 
UNION SELECT 2,5,9,0,1 
UNION SELECT 3,5,9,0,1 
UNION SELECT 3,6,9,1,0 

ich alle Datensätze erhalten möchten (aktuell oder nicht) für die Personen, bei denen sich alle ihre Einschreibung am gleichen Ort befinden (mit der gleichen LocationId), aber nur die Werte, die aktuell sind (IsEnrolled = 1), wenn die Standorte unterschiedlich sind. Für die gleiche PersonId möchte ich alle Datensätze, wenn die LocationId eindeutig ist, und nur die aktuelle (IsEnrolled = 1), wenn die LocationId nicht eindeutig für die PersonId ist.

Daten Ich will zurück aus einer Abfrage erhalten:

SELECT  
    1 AS PersonId, 
    6 AS ClassId, 
    1 As IsEnrolled, 
    0 AS IsExited 
UNION SELECT 2, 8, 1, 0 
UNION SELECT 2, 9, 0, 1 
UNION SELECT 3, 9, 1, 0 
+0

Erstens können Sie festlegen, wie Sie „Menschen bestimmen "? Ist "Id" die Personen-ID? Sonst kann ich nicht sehen, wie Personen über diese Datensätze hinweg verfolgt werden. Kannst du auch eine detailliertere Definition dessen geben, was du unter "alle ihre Registrierung ist am selben Ort?" Versteht das bedeutet, wo für jede Gruppe (aka Fenster) von Datensatz, wo Id (vorausgesetzt, dies ist die PersonId ist) das gleiche ist und LocationID ist das gleiche, alle Datensätze in diesem Fenster sind markiert IsEnrolled –

+0

@magnum_pi - umbenannt Id für Klärung PersonId und "am gleichen Ort" bedeutet, dass die LocationId in der Zeile gleich ist. Danke – Noel

+0

Die Beschreibung Ihrer gewünschten Ergebnisse ist mir unklar."Alle Datensätze, bei denen sich alle Anmeldungen am selben Ort befinden". Aus Ihren Beispieldaten sollte sich nur Person2 qualifizieren. Und "aktuell, wo Orte anders sind", ich weiß nicht, was das überhaupt bedeutet. –

Antwort

1

Ich denke, es ist eine Menge einfacher als zuvor versuchte Methoden:

SELECT Test.* 
FROM @Test Test 
JOIN 
    (
     SELECT PersonID, LocationID 
     FROM @Test T 
     WHERE ISENROLLED = 1 
     GROUP BY PeronID, LocationID 
    ) T 
    ON Test.PersonID = T.PersonID 
    AND Test.LocationID = T.LocationID 

Da kann man nur hav Wenn Sie einen "isenrolled" Datensatz pro Person eingeben, wird garantiert, dass die innere Abfrage eine Person/Standort-Kombination für jede Person zurückgibt. Wenn Sie also persönlich und an Ihrem Standort teilnehmen, können Sie sicherstellen, dass Sie jeden Datensatz für diese Person erhalten, der sich am Standort der aktuell angemeldeten Klasse befand.

+0

Vielleicht. Ich verstehe die Aufgabe als "nimm den Strom" plus "nimm die anderen, wenn sie alle am selben Ort sind wie der Strom". Sie verstehen es als "nehmen Sie die aktuelle" plus "nehmen Sie die anderen, die an der gleichen Stelle wie aktuell sind". –

+0

@ThorstenKettner Ja, es gibt eine Sprachbarriere und er hat seine Frage nicht geklärt. –

+1

Die tatsächliche Abfrage war ein bisschen komplexer als das, was ich gepostet habe, aber diese Antwort hat mich dahin gebracht, wo ich sein musste. – Noel

1

Hier ist ein Ansatz, den Sie verwenden:

SELECT * 
FROM 
(
    SELECT 
     *, 
     (SELECT COUNT(DISTINCT t2.LocationID) FROM tab t2 WHERE t2.PersonId = t1.PersonId) AS LocationCount 
    FROM tab t1 
) t 
WHERE 
    t.LocationCount = 1 --get all records for those at the same location 
    OR (t.LocationCount > 1 AND t.IsEnrolled = 1) --get only current records for those @ multiple location 

Im Wesentlichen zählen Sie die Standorte eine Person an eingeschrieben ist. Wenn sie nur eins sind, nehmen Sie alle Datensätze, und wenn mehrere nur die aktuelle Registrierung nehmen.

+0

Eine gute Lösung. Stellen Sie "CASE WHEN" jedoch niemals in "WHERE" -Klauseln. Es kompliziert nur die Klausel. Ihre 'WHERE'-Klausel ist einfach:' WHERE LocationCount = 1 ODER (LocationCount> 1 UND IsEnrolled = 1) ', die immer noch vereinfacht werden kann auf' WHERE LocationCount = 1 ODER IsEnrolled = 1'. –

+0

@ThorstenKettner Sie haben Recht. Ich dachte darüber nach. Es ist jetzt korrigiert. – morgb

0

Sie können die personid s erhalten eine Aggregation Abfrage:

select personid 
from @test t 
where isenrolled = 1 
group by personid 
having min(location) = max(location); 

Zu diesem Zeitpunkt genug ist es einfach in zu verwenden alles andere zu bekommen:

select t.* 
from @test t 
where personid in (select personid 
        from @test t 
        where isenrolled = 1 
        group by personid 
        having min(location) = max(location) 
       ); 
+0

Diese Abfrage gibt immer alle Datensätze zurück, bei denen IsEnrolled = 1 ist. Durch das Entfernen der WHERE-Klauseln werden alle Datensätze abgerufen, bei denen der Speicherort derselbe ist, sodass ich sie in einer zweiteiligen Abfrage abrufen kann. – Noel

+0

@Noel. . . Entfernen Sie einfach die Bedingung 'isenrolled' von der äußeren Abfrage. Ich war mir nicht sicher, welche Aufzeichnungen du zurückgeben wolltest. –

1

Wenn ich richtig verstehen Sie alle Datensätze, die entweder Strom oder gehören zu einer Person, die immer an der gleichen Stelle war. Daraus folgt:

select personid, classid, isenrolled, isexited 
from mytable 
where isenrolled = 1 
or personid in 
(
    select personid 
    from mytable 
    group by personid 
    having min(locationid) = max(locationid) 
) 
order by personid, classid; 

Das gleiche mit Fensterfunktionen, so dass die Tabelle lesen gerade einmal hat sein:

select personid, classid, isenrolled, isexited 
from 
(
    select 
    personid, classid, isenrolled, isexited, 
    min(locationid) over (partition by personid) as minloc, 
    max(locationid) over (partition by personid) as maxloc 
    from mytable 
) 
where isenrolled = 1 or minloc = maxloc 
order by personid, classid; 
0

ein

beitreten
select t2.PersonId, t2.ClassId, t2.IsEnrolled, t2.IsExited 
    from @Test t1 
     left join @Test t2 
      on t2.PersonId = t1.PersonId 
       and ((t2.IsEnrolled = 0 and t2.LocationId <> t1.LocationId) 
        or (t2.IsEnrolled = 1) 
       ) 
    where t1.IsEnrolled = 1 
Verwandte Themen