2013-06-15 6 views
7

Ich möchte männliche, weibliche und insgesamt Studenten aus Student Tabelle für ein bestimmtes Jahr angegeben. Ich möchte, wenn das Ergebnis in Form angezeigt werden:Anzahl männlich, weiblich und insgesamt

==================================== 
| Label | Value | Year  | 
==================================== 
| Male  | 0  | 2013  | 
| Female | 23  | 2013  | 
| Total | 23  | 2013  | 
==================================== 

Die Abfrage sollte 0 angezeigt, wenn es keine männliche/weibliche Anpassung für das angegebene Jahr. Irgendeine Idee, wie ich das erreichen kann?

Vielen Dank im Voraus

+0

Vielleicht können Sie bieten DDL und Ihre aktuelle Abfrage? – Nikola

+0

Schülertisch (id, name, gender, registeredYear) – aby

+0

So viele verschiedene Optionen für Sie zur Auswahl :) Liebst du nicht nur SQL! – KyleK

Antwort

8

Betrachten Sie folgende Abfrage:

select 
    max(registeredYear) as year, 
    count(case when gender='Male' then 1 end) as male_cnt, 
    count(case when gender='Female' then 1 end) as female cnt, 
    count(*) as total_cnt 
from student 
where registeredYear = 2013 
group by registeredYear; 

Das Ergebnis wird so sein:

Year male_cnt female_cnt total_cnt 
---- -------- ---------- --------- 
2013  0   23  23 

Sie dieses Ergebnis in die Form umwandeln können Sie wollen. Wenn Sie es in einer Abfrage tun wollen, dann können Sie es wie folgt tun:

with t as (
    select 
     max(registeredYear) as year, 
     count(case when gender='Male' then 1 end) as male_cnt, 
     count(case when gender='Female' then 1 end) as female cnt, 
     count(*) as total_cnt 
    from student 
    where registeredYear = 2013 
    group by registeredYear) 
select 'Male', male_cnt as male, year from t 
union all 
select 'Female', female_cnt as male, year from t 
union all 
select 'Total', total_cnt as male, year from t 
; 
+0

Danke für die Antwort. Aber es wird nichts zurückgeben, wenn ich das Jahr umstelle, sagen wir 2014, wo es keine Daten gibt. Ich erwartete 2014 | 0 | 0 | 0 – aby

+0

@aby 'count()' Funktion wird immer die Nummer zurückgeben. Für den Fall, dass keine Daten vorhanden sind, die die WHERE-Bedingungen erfüllen, wird "0" zurückgegeben. Aber der Jahrteil wird leer sein ... – ntalbs

+0

@aby Vielleicht können Sie 'coalesce()' Funktion verwenden und Variable wie 'max binden (coalesce (registredYear,: inputYear))' ... wobei ': input_year' bind Variable ist Sie sollten angeben. Ich bin mir nicht sicher, wie SQL Server Bind-Variable verwendet, aber Sie würden das wissen. – ntalbs

0

Gerade diese Abfrage ausführen ...

SELECT 
    MAX(registeredYear) as Year 
    ,SUM(CASE WHEN gender = 'Male' THEN 1 END) AS Male 
    ,SUM(CASE WHEN gender = 'Female' THEN 1 END) AS Female 
    ,SUM(CASE WHEN gender IS NOT NULL THEN 1 ELSE 0 END) AS Total 
FROM from student 
WHERE registeredYear = 2013 
GROUP BY registeredYear; 
0

etwas wie folgt aus:

select 'Male' as Label, count(gender) as Value from student where gender= 'Male' 
union (
select 'Female' as Label, count(gender) as Value from student where gender= 'Female') 
union (
select 'Total' as Label, count(gender) as Value from student) 
+0

Diese Abfrage liest die Tabelle dreimal, was ineffizient ist. – ntalbs

0

Versuchen Sie dies, unter der Annahme, dass kein Nullwert in Geschlecht oder Registriert istJahr:

+0

Oh, das bekommt eine Zusammenfassung für jedes Jahr in der Tabelle. Sie wollen ein bestimmtes Jahr. Ersetzen Sie dazu AllYears als 'AllYears AS (SELECT 2014 AS RegisteredYear)' –

+0

Danke, Ihre Lösung ist so nah. Aber was soll ich ändern, damit Female | 0 | 2014, Male | 0 | 2014, Total | 0 | 2014 zurückgegeben wird, wenn keine Daten für das ausgewählte Jahr zurückgegeben werden? – aby

+0

Ah, ich hatte früher den gleichen Fehler gemacht mit den Gender/Jahr-Zählungen (es gab '1' wenn keine), aber ich habe es jetzt auf die gleiche Weise gelöst. –

0

Hier ist eine weitere Variante mit UNPIVOT. Dieser sucht spezifisch nur für MÄNNER und WEIBLICH, also ist es nicht so flexibel wie mein anderer (da Sie jedes Geschlecht fest codieren müssen). Aber es ist wahrscheinlich das effizienteste.

WITH AllYears (RegisteredYear) AS 
(
    --SELECT DISTINCT RegisteredYear 
    --FROM Student 

    --...OR... 

    SELECT 2014 
) 
, GenderAndYearCounts AS 
(
    SELECT RegisteredYear 
     , SUM(CASE Gender WHEN 'MALE' THEN 1 ELSE 0 END) MaleCount 
     , SUM(CASE Gender WHEN 'FEMALE' THEN 1 ELSE 0 END) FemaleCount 
     , COUNT(*) YearCount 
    FROM Student 
    GROUP BY RegisteredYear 
) 
, GenderAndYearCountsForAllYears AS 
(
    SELECT AllYears.RegisteredYear 
     , ISNULL(MaleCount, 0) AS MaleCount 
     , ISNULL(FemaleCount, 0) AS FemaleCount 
     , ISNULL(YearCount, 0) AS YearCount 
    FROM AllYears 
     LEFT JOIN GenderAndYearCounts ON GenderAndYearCounts.RegisteredYear = AllYears.RegisteredYear 
) 

SELECT Label, Value, RegisteredYear 
FROM 
(
    SELECT RegisteredYear, MaleCount AS Male, FemaleCount AS Female, YearCount AS Total 
    FROM GenderAndYearCountsForAllYears 
) allCounts 

UNPIVOT 
(
    Value FOR Label IN (Male, Female, Total) 
) unpivotted 
0

Alle Geschlechter, dann all die Jahre, dann die Grafen:

declare @Year int 
set @Year = 2014 

select labels.label, 
    counts.cnt, 
    @Year as registeredYear 
    from 
     (select 'Male' as label, 1 as sortOrder 
     union all 
     select 'Female', 2 
     union all 
     select 'All', 3) as labels 
    left join 
     (select gender, 
     count(1) cnt 
     from student 
     where registeredYear = @Year 
     group by gender) as counts 
    on labels.label = counts.gender  
    order by labels.sortOrder 
0

Da Sie shouldn't mix grid formatting with data retrieval

SELECT 
    SUM(CASE WHEN gender = 'Male' THEN 1 ELSE 0 END) as MaleCount, 
    SUM(CASE WHEN gender = 'Female' THEN 1 ELSE 0 END) as FemaleCount, 
    COUNT(*) as TotalCount 
FROM student 
WHERE registeredYear = 2013 
1

Ich glaube, das etwa so effizient ist, wie Sie mit nur bekommen kann ein einziger Durchgang durch den Schülertisch. Ändern Sie einfach das Jahr im Jahr CTE nach Bedarf.

with 
year as 
(
    select '2013' year 
), 
gender as (
    select 'Male' gender 
    union all 
    select 'Female' gender 
) 
select coalesce(g.gender,'Total') "Label", 
     count(s.gender) "Value", 
     y.year "Year" 
    from gender g 
    cross join year y 
    left join student s 
    on s.gender = g.gender 
    and s.year = y.year 
group by grouping sets((g.gender, y.year), (y.year)) 
order by case g.gender when 'Male' then 1 when 'Female' then 2 else 3 end 
; 

Ein vollständig normalisiertes Datenmodell wird wahrscheinlich sowohl eine Schuljahr- als auch eine Geschlechtstabelle haben, so dass die CTEs nicht benötigt würden. (es sei denn, Sie möchten wirklich Zeilen für Jahre zurückgeben, die keine Daten haben)

Hier ist eine bloße sqlfiddle Demonstration ohne Kursteilnehmeridentifikation und -name, da sie für das Problem zur Hand sind.

1

Ihre Anfrage scheint sehr einfach, aber es hat zwei Komplikationen. Der erste ist, dass eine Zeile eine Zusammenfassung der anderen beiden ist. Dies schlägt vor, rollup oder grouping sets in der Abfrage zu verwenden.

Die zweite ist die Anforderung, Werte zu haben, auch wenn Sie keine Daten haben. Dies deutet auf die Verwendung einer "Treiber" -Unterabfrage hin. Eine solche Unterabfrage definiert alle Zeilen in der Ausgabe, bevor Werte zugewiesen werden. Sie verwenden eine Treibertabelle mit left outer join.

Eine unausgesprochene Anforderung könnte sein, das Jahr nur einmal zu erwähnen.

Der folgende Ansatz für die Abfrage fügt die endgültige Form für das Jahr zusammen. Die dann schließt sich links die Zusammenfassung, zieht Werte von dort gegebenenfalls:

with year as (
     select 2013 as Year 
    ) 
select driver.label, coalesce(s.value, 0) as Value, driver.Year 
from ((select 'Male' as label, year from year 
    ) union all 
     (select 'Female' as label, year from year 
    ) union all 
     (select 'Total' as label, year from year 
    ) 
    ) driver left outer join 
    (select coalesce(Gender, 'Total') as Gender, year.year, count(*) as value 
     from Students cross join year 
     group by Gender with Rollup 
    ) s 
    on driver.year = s.year; 

Dies setzt voraus, dass das Geschlecht als „männlich“ dargestellt wird, und „Female“ und dass es eine Spalte year in den Daten (ohne Abtastwerteingang genannt oder Tabellenformate muss man auf Spaltennamen und Beispielwerte raten).

+0

Ich mag Ihre Erklärung, aber Ihre Implementierung ist abgehört und ich denke übermäßig kompliziert. Die Unterabfrage "s" fehlt Jahr für Gruppe und Summe enthält fälschlicherweise alle Jahre. Die Outer-Join-Bedingung driver.gender existiert nicht (sollte driver.label sein), und die Zeile Total wird niemals mit dem Jahr übereinstimmen (2013 vs. null). Siehe [meine frühere Antwort] (http://stackoverflow.com/a/17119899/1012053) für eine einfachere Arbeitsabfrage. – dbenham

+0

@dbenham. . . Als ich mir die Antworten anschaute, suchte ich nach einer, die "union" und "rollup" hatte und aufgrund der "Gruppierungs-ses" wahrscheinlich deine verpasst hat. Es schien, dass keine der Antworten die richtigen Bedenken ansprach, weshalb ich antwortete (und dann versuchte, die Antwort zu vereinfachen, die zu den Problemen führte, die Sie notieren). Ich habe Ihre Antwort ordnungsgemäß hochgestuft, weil dies der geeignete Ansatz ist. –

0

Das funktionierte für mich. Aber noch konnte es nicht 0 seit Jahren für beide M und F anzuzeigen, in dem keine Daten vorhanden sind:

Select * from 
(
SELECT isnull (SUM(CASE WHEN gender = 'M' THEN 1 ELSE 0 END),0) as Male, 
     isnull(SUM(CASE WHEN gender = 'F' THEN 1 ELSE 0 END),0) as Female, 
     registeredYear as 'year' 

FROM student 
WHERE registeredDate.Year = 2013 //could be a variable 
group by registeredYear 
) as s 

UNPIVOT 
( 
value FOR gender IN (Male, Female) 
) Sub 
3

sollten Sie verwenden:

select name, COUNT(*)as tot, 
    COUNT(case when details.gender='male' then 1 end) as male, 
    COUNT(case when details.gender='female' then 1 end) as female 
    from details group by name 
+0

Es funktioniert gut .... –

0
select sp.CLASS_Name , count(*) as total 
    , sum(case when si.STDNT_GENDER = 1 then 1 else 0 end) as Male 
    , sum(case when si.STDNT_GENDER = 0 then 1 else 0 end) as Female 
    from SCHOOL_PLANE sp inner join STUDENT_INFO si 
on sp.CLASS_ID=si.STDNT_CLASS_PLANE_ID group by sp.CLASS_Name 

------- 
select sp.CLASS_Name , count(*) as total 
    , sum(case si.STDNT_GENDER when 1 then 1 else 0 end) as Male 
    , sum(case si.STDNT_GENDER when 0 then 1 else 0 end) as Female 
    from SCHOOL_PLANE sp inner join STUDENT_INFO si 
on sp.CLASS_ID=si.STDNT_CLASS_PLANE_ID group by sp.CLASS_Name 
------------ 
select sp.CLASS_Name , count(*) as total 
    , count(case when si.STDNT_GENDER = 1 then 1 end) as Male 
    , count(case when si.STDNT_GENDER = 0 then 1 end) as Female 
    from SCHOOL_PLANE sp inner join STUDENT_INFO si 
on sp.CLASS_ID=si.STDNT_CLASS_PLANE_ID group by sp.CLASS_Name 
+1

Sie könnten Ihre Antwort verbessern, indem Sie eine kurze Beschreibung Ihrer Lösung hinzufügen. – dakab

Verwandte Themen