Can't use group by in SQL Server - sql

I am learning how to use Group By in SQL Server and I am trying to write a Query that would let me get all the information from Alumns in a table in numbers.
My table is like the following:
Name | Alumn_ID | Course | Credits | Passed
Peter 1 Math 2 YES
John 2 Math 3 YES
Thomas 3 Math 0 NO
Peter 1 English 3 YES
Thomas 2 English 2 YES
John 3 English 0 NO
The result I want is the following one:
Alumn | Total_Credits | Courses | Passed | Not_Passed
Peter 5 2 2 0
John 5 2 2 0
Thomas 0 2 0 2
I know that I have to use Group By and COUNT but I'm stuck since I'm a beginner, I really don't know how can I separate Passed and Not_Passed in the result from the PASSED column in the table, thanks in advance

SELECT t.id, t.name AS alum,
SUM(credits) AS total_credits,
COUNT(*) AS courses,
SUM(CASE WHEN Passed = 'YES' THEN 1 ELSE 0 END) AS Passed,
SUM(CASE WHEN Passed = 'NO' THEN 1 ELSE 0 END) AS Reprobated
FROM t
GROUP BY t.id, t.name
I assume reprobated means not passed.

The example below will do that like you solicited.
create table Alumns
(
Name varchar(30) not null
,Alumn_Id int not null
,Course varchar(30) not null
,Credits int not null
,passed varchar(3) not null
)
GO
insert into Alumns
(Name, Alumn_ID, Course, Credits, Passed)
values
('Peter', 1, 'Math', 2, 'YES')
,('John', 2, 'Math', 3, 'YES')
,('Thomas', 3, 'Math', 0, 'NO')
,('Peter', 1, 'English', 3, 'YES')
,('John', 2, 'English', 2, 'YES')
,('Thomas', 3, 'English', 0, 'NO')
GO
select al.Alumn_Id,al.Name
, Sum(al.Credits) as [Total Credits]
, Count(al.Course) as Courses
, Sum(case al.passed when 'YES' then 1 else 0 end) as Passed
, Sum(case al.passed when 'NO' then 1 else 0 end) as [Not Passed]
from dbo.Alumns al
group by al.Alumn_Id, al.Name
but note you will get an error because you data is incorrect.
Look at your own example where John and Peter are with wrong Ids for the Math/English rows.
That way you will never end with the correct result and that's why it's a good practice to group by Ids.
Edit
I see you corrected your example data yes that way will fetch the exact results you want.

You can separate Passed and Not_Passed using a CASE function.
SELECT MAX([name]) AS [Name],
SUM(Credits) AS Total_Credits,
COUNT(Course) AS Courses,
SUM(CASE WHEN Passed='Yes' THEN 1 ELSE 0 END) AS Passed,
SUM(CASE WHEN Passed='No' THEN 1 ELSE 0 END) AS Not_Passed
FROM TableName
GROUP BY Alumn_ID
However, I do not think that values of your tables (both table) are correct. Please check them again. For example, according to your table, John has two Alumn_IDs (both 2 and 3). If these are two different Johns, then your desired outcome should be changed.
Result
+--------+---------------+---------+--------+------------+
| Name | Total_Credits | Courses | Passed | Not_Passed |
+--------+---------------+---------+--------+------------+
| Peter | 5 | 2 | 2 | 0 |
| John | 3 | 1 | 1 | 0 |
| Thomas | 2 | 3 | 1 | 2 |
+--------+---------------+---------+--------+------------+

Related

Sum (or count) multiple case statement

I have the following table:
Month | Item | Events | Party | Spirit | Faith |
May | 123 | 1 | 1 | 0 | 0 |
June |123 | 1 | 0 | 1 | 1 |
it is basically 1 for yes 0 for no. I need to know how many different categories each item is in each month
I need the following results:
Month | Item | Counts |
May | 123 | 2 |
June| 123 | 3 |
This is NOT working:
select Month, Item,
sum(case when EVENTS = 1 then 1 when PARTY = 1 then 1 when SPIRIT = 1 then 1 when FAITH = 1 then 1 else 0 end) as Counts
from TABLE
group by 1,2
Please help, thanks!
You don't need aggregation:
select Month, Item,
(events + party + spirit + faith) as counts
from t;
CREATE TABLE #T
(
Month varchar(10), Item int, Events bit, Party bit, Spirit bit , Faith bit
)
insert into #T
SELECT 'May' , 123 , 1 , 1 , 0 , 0 union
SELECT 'June' ,123 , 1 , 0 , 1 , 1
select Month, Item, CAST(Events AS INT) + CAST(Party AS INT)+ CAST(Spirit AS
INT) +CAST(Faith AS INT) from #T
Aggregation is not needed. Since the events, party, spirit and faith are bit columns, we need to cast it to int and then add it.

Count who paid group by 1, 2 or 3+

I have a payment table like the example below and I need a query that gives me how many IDs paid (AMOUNT > 0) 1 time, 2 times, 3 or more times. Example:
+----+--------+
| ID | AMOUNT |
+----+--------+
| 1 | 50 |
| 1 | 0 |
| 2 | 10 |
| 2 | 20 |
| 2 | 15 |
| 2 | 10 |
| 3 | 80 |
+----+--------+
I expect the result:
+-----------+------------+-------------+
| 1 payment | 2 payments | 3+ payments |
+-----------+------------+-------------+
| 2 | 0 | 1 |
+-----------+------------+-------------+
ID 1: Paid 1 time (50). The other payment is 0, so I did not count. So, 1 person paid 1 time.
ID 2: Paid 3 times (10,20,15). So, 1 person paid 3 or more time.
ID 3: Paid 1 time (80). So, 2 persons paid 1 time.
I'm doing manually on excel right now but I'm pretty sure there is a more practical solution. Any ideas?
A little sub-query will do the trick
Declare #YOurTable table (ID int, AMOUNT int)
Insert into #YourTable values
( 1 , 50 ),
( 1 , 0) ,
( 2 , 10) ,
( 2 , 20) ,
( 2 , 15) ,
( 2 , 10) ,
( 3 , 80)
Select [1_Payment] = sum(case when Cnt=1 then 1 else 0 end)
,[2_Payment] = sum(case when Cnt=2 then 1 else 0 end)
,[3_Payment] = sum(case when Cnt>2 then 1 else 0 end)
From (
Select id
,Cnt=count(*)
From #YourTable
Where Amount<>0
Group By ID
) A
Returns
1_Payment 2_Payment 3_Payment
2 0 1
To get the output you want try using a table to form the data and then SELECT from that:
with c as (
select count(*) count from mytable where amount > 0 group by id)
select
sum(case count when 1 then 1 else 0 end) "1 Payment"
, sum(case count when 2 then 1 else 0 end) "2 Payments"
, sum(case when count > 2 then 1 else 0 end) "3 Payments"
from c
Here is an example you can play with to see how the query is working.

SQL query for sales report by date

I have a table of sales leads:
CREATE TABLE "lead" (
"id" serial NOT NULL PRIMARY KEY,
"marketer" varchar(500) NOT NULL,
"date_set" varchar(500) NOT NULL
)
;
INSERT INTO lead VALUES (1, 'Joe', '05/01/13');
INSERT INTO lead VALUES (2, 'Joe', '05/02/13');
INSERT INTO lead VALUES (3, 'Joe', '05/03/13');
INSERT INTO lead VALUES (4, 'Sally', '05/03/13');
INSERT INTO lead VALUES (5, 'Sally', '05/03/13');
INSERT INTO lead VALUES (6, 'Andrew', '05/04/13');
I want to produce a report that summarizes the number of records each marketer has for each day. It should look like this:
| MARKETER | 05/01/13 | 05/02/13 | 05/03/13 | 05/04/13 |
--------------------------------------------------------
| Joe | 1 | 1 | 1 | 0 |
| Sally | 0 | 0 | 2 | 1 |
| Andrew | 0 | 0 | 0 | 1 |
What's the SQL query to produce this?
I have this example set up on SQL Fiddle: http://sqlfiddle.com/#!12/eb27a/1
Pure SQL cannot produce such structure (it is two dimensional, but sql return plain list of records).
You could make query like this:
select marketer, date_set, count(id)
from lead
group by marketer, date_set;
And vizualise this data by your reporting system.
You can do it like this:
select
marketer,
count(case when date_set = '05/01/13' then 1 else null end) as "05/01/13",
count(case when date_set = '05/02/13' then 1 else null end) as "05/02/13",
count(case when date_set = '05/03/13' then 1 else null end) as "05/03/13",
count(case when date_set = '05/04/13' then 1 else null end) as "05/04/13"
from lead
group by marketer

Split Multiple Columns into Multiple Rows

I have a table with this structure.
UserID | UserName | AnswerToQuestion1 | AnswerToQuestion2 | AnswerToQuestion3
1 | John | 1 | 0 | 1
2 | Mary | 1 | 1 | 0
I can't figure out what SQL query I would use to get a result set like this:
UserID | UserName | QuestionName | Response
1 | John | AnswerToQuestion1 | 1
1 | John | AnswerToQuestion2 | 0
1 | John | AnswerToQuestion3 | 1
2 | Mary | AnswerToQuestion1 | 1
2 | Mary | AnswerToQuestion2 | 1
2 | Mary | AnswerToQuestion3 | 0
I'm trying to split the three columns into three separate rows. Is this possible?
SELECT
Y.UserID,
Y.UserName,
QuestionName = 'AnswerToQuestion' + X.Which,
Response =
CASE X.Which
WHEN '1' THEN AnswerToQuestion1
WHEN '2' THEN AnswerToQuestion2
WHEN '3' THEN AnswerToQuestion3
END
FROM
YourTable Y
CROSS JOIN (SELECT '1' UNION ALL SELECT '2' UNION ALL SELECT '3') X (Which)
This performs equally well to UNPIVOT (sometimes better) and works in SQL 2000 as well.
I took advantage of the questions' similarity to create the QuestionName column, but of course this will work with varying question names.
Note that if your list of questions is long or the question names are long, you might experiment with 2 columns in the X table, one for the question number and one for the question name. Or if you already have a table with the list of questions, then CROSS JOIN to that. If some questions are NULL then easiest is to put the above query in a CTE or derived table and then add WHERE Response IS NOT NULL.
Assuming SQL Server 2005+ you can use UNPIVOT
;with YourTable as
(
SELECT 1 UserID,'John' UserName,1 AnswerToQuestion1,0 AnswerToQuestion2,1 AnswerToQuestion3
UNION ALL
SELECT 2, 'Mary', 1, 1, 0
)
SELECT UserID, UserName, QuestionName, Response
FROM YourTable
UNPIVOT
(Response FOR QuestionName IN
(AnswerToQuestion1, AnswerToQuestion2,AnswerToQuestion3)
)AS unpvt;
According to Itzik Ben-Gan in Inside Microsoft SQL Server 2008: T-SQL Querying, SQL Server goes through three steps when unpivoting a table:
Generate copies
Extract elements
Remove rows with NULLs
Step 1: Generate copies
A virtual table is created that has a copy of each row in the orignal table for each column that is being unpivoted.
Also, a character string of the column name is stored in a new column (call this the QuestionName column). *Note: I modified the value in one of your columns to NULL to show the full process.
UserID UserName AnswerTo1 AnswerToQ2 AnswerToQ3 QuestionName
1 John 1 0 1 AnswerToQuestion1
1 John 1 0 1 AnswerToQuestion2
1 John 1 0 1 AnswerToQuestion3
2 Mary 1 NULL 1 AnswerToQuestion1
2 Mary 1 NULL 1 AnswerToQuestion2
2 Mary 1 NULL 1 AnswerToQuestion3
Step 2: Extract elements
Then another table is created that creates a new row for each value from the source column which corresponds
to the character string value in the QuestionName column. The value is stored in a new column (call this the Response column).
UserID UserName QuestionName Response
1 John AnswerToQuestion1 1
1 John AnswerToQuestion2 0
1 John AnswerToQuestion3 1
2 Mary AnswerToQuestion1 1
2 Mary AnswerToQuestion2 NULL
2 Mary AnswerToQuestion3 1
Step 3: Remove rows with NULLS
This step filters out any rows that were created with null values in the Response column. In other words,
if any of the AnswerToQuestion columns had a null value, it would not be represented as an unpivoted row.
UserID UserName QuestionName Response
1 John AnswerToQuestion1 1
1 John AnswerToQuestion2 0
1 John AnswerToQuestion3 1
2 Mary AnswerToQuestion1 1
2 Mary AnswerToQuestion3 1
If you follow those steps, you can
CROSS JOIN all rows in the table against each AnswerToQuestion
column name to get row copies
Populate the Response column based
on the matching the source column and QuestionName
Remove the NULLs to get the same
results without using UNPIVOT.
An example below:
DECLARE #t1 TABLE (UserID INT, UserName VARCHAR(10), AnswerToQuestion1 INT,
AnswertoQuestion2 INT, AnswerToQuestion3 INT
)
INSERT #t1 SELECT 1, 'John', 1, 0, 1 UNION ALL SELECT 2, 'Mary', 1, NULL, 1
SELECT
UserID,
UserName,
QuestionName,
Response
FROM (
SELECT
UserID,
UserName,
QuestionName,
CASE QuestionName
WHEN 'AnswerToQuestion1' THEN AnswerToQuestion1
WHEN 'AnswerToQuestion2' THEN AnswertoQuestion2
ELSE AnswerToQuestion3
END AS Response
FROM #t1 t1
CROSS JOIN (
SELECT 'AnswerToQuestion1' AS QuestionName
UNION ALL SELECT 'AnswerToQuestion2'
UNION ALL SELECT 'AnswerToQuestion3'
) t2
) t3
WHERE Response IS NOT NULL

Need some sort of "conditional grouping" in MySQL

I have Article table:
id | type | date
-----------------------
1 | A | 2010-01-01
2 | A | 2010-01-01
3 | B | 2010-01-01
Field type can be A, B or C.
I need to run a report that would return how many articles of each type there is per every day, like this:
date | count(type="A") | count(type="B") | count(type="C")
-----------------------------------------------------
2010-01-01 | 2 | 1 | 0
2010-01-02 | 5 | 6 | 7
Currently I am running 3 queries for every type and then manually merging the results
select date, count(id) from article where type="A" group by date
Is it possible to do this in one query? (in pure sql, no stored procedures or anything like that).
Thanks
A combination of SUM and CASE should do ya
select date
, sum(case when type ='A' then 1 else 0 end) as count_type_a
, sum(case when type ='B' then 1 else 0 end) as count_type_b
, sum(case when type ='C' then 1 else 0 end) as count_type_c
from article group by date
EDIT: Alex's answer above uses a better approach that the one in this answer. I'm leaving it here just because it also satisfies the question, in an alternative way:
You should be able to use sub queries, as follows:
SELECT DATE(a.date) as date,
(SELECT COUNT(a1.id) FROM articles a1 WHERE a1.type = 'A' AND a1.date = a.date) count_a,
(SELECT COUNT(a2.id) FROM articles a2 WHERE a2.type = 'B' AND a2.date = a.date) count_b,
(SELECT COUNT(a3.id) FROM articles a3 WHERE a3.type = 'C' AND a3.date = a.date) count_c
FROM articles a
GROUP BY a.date;
Test Case:
CREATE TABLE articles (id int, type char(1), date datetime);
INSERT INTO articles VALUES (1, 'A', '2010-01-01');
INSERT INTO articles VALUES (2, 'A', '2010-01-01');
INSERT INTO articles VALUES (3, 'B', '2010-01-01');
INSERT INTO articles VALUES (4, 'B', '2010-01-02');
INSERT INTO articles VALUES (5, 'B', '2010-01-02');
INSERT INTO articles VALUES (6, 'B', '2010-01-03');
INSERT INTO articles VALUES (7, 'B', '2010-01-01');
INSERT INTO articles VALUES (8, 'C', '2010-01-05');
Result:
+------------+---------+---------+---------+
| date | count_a | count_b | count_c |
+------------+---------+---------+---------+
| 2010-01-01 | 2 | 2 | 0 |
| 2010-01-02 | 0 | 2 | 0 |
| 2010-01-03 | 0 | 1 | 0 |
| 2010-01-05 | 0 | 0 | 1 |
+------------+---------+---------+---------+
4 rows in set (0.00 sec)