group by + counting different column values - sql

I have a simple table with id, gender, age and favoriteMovie.
Gender column has values male and female only.
I want to display all movie titles from movies column (group by movies) and in two separated columns number of males and number of females voting on selected movie.
Something like count(gender = 'male') as male, count(gender = 'female') as female
Database is ms-sql 2008
Any sugestion greatly appreciated

You can use case syntax (while change count into sum):
select favoriteMovie as movie,
sum(case
when gender = 'male' then
1
else
0
end) as male,
sum(case
when gender = 'female' then
1
else
0
end) as female
from MyTable
group by favoriteMovie

Not very good with pivots, but this worked for me.
declare #t table (id varchar(55),gender varchar(55),age varchar(55),favoriteMovie varchar(55))
insert into #t values(1,'m',16,'star wars')
insert into #t values(2,'f',16,'star trek')
insert into #t values(3,'m',16,'star trek')
insert into #t values(4,'f',16,'star wars')
insert into #t values(5,'m',16,'star wars')
And then query.
select favoriteMovie, sum([m]) as "m",sum([f]) as "f"
from #t
pivot
(
count(gender)
for gender in ([m],[f])
) as pt
group by favoriteMovie

Related

Split one column into 2 columns based on the last character of the column

I have a column region_no in a table. The column can hold 2 kinds of values - one set of values that end with the letter 'R' and the other that end with letter 'B'. This column must be split into 2 columns based on the last letter.
The create table and insert of sample data is :
CREATE TABLE test_17Jan
(
Region_No varchar(8),
Customer_Name varchar(20),
City varchar(20),
zip_code varchar(10)
)
INSERT INTO test_17Jan VALUES ('101R', 'John Doe', 'Detroit', '48127')
INSERT INTO test_17Jan VALUES ('202B', 'John Doe', 'Detroit', '48127')
INSERT INTO test_17Jan VALUES ('201B', 'Tim Smith', 'Waunakee', '53597')
The desired output is :
Customer_Name
City
zip_code
Inside_Sales_Region
B2B_Region
John Doe
Detroit
48127
101R
202B
Tim Smith
Waunakee
53597
NULL
201B
I thought of pivot function, but that needs to have an aggregate. Is there a way to get the output in the above format? Any help will be appreciated. The code will run on SQL Server 2019 (v15).
You can use conditional aggregation to get your desired results:
select Customer_Name, City, zip_code,
Max(Inside_Sales_Region) Inside_Sales_Region,
Max(B2B_Region) B2B_Region
from (
select Customer_Name, City, zip_code,
case when Right(Region_No,1) = 'R' then Region_No end Inside_Sales_Region,
case when Right(Region_No,1) = 'B' then Region_No end B2B_Region
from test_17Jan
)t
group by Customer_Name, City, zip_code;
better option should be LEFT SELF JOIN. But this should also work.
SELECT
CUSTOMER_NAME, CITY, ZIP_CODE,
CASE
WHEN Region_No like '%R' THEN Region_No
ELSE NULL AS Inside_Sales_Region,
CASE
WHEN Region_No like '%B' THEN Region_No
ELSE NULL AS B2B_Region
FROM test_17Jan;

Not able to add row wise data in column form

EmpID (Primary Key) Sale Items Paid
ABC chair Yes
WXY chair Under Review
PER Laptop Yes
ABC Chair Yes
Now i want to create another table where i want to insert data Like below
Emp ID Chair Laptop
ABC 2 0
WXY 1 0
My query to insert is
Select Emp Id from EMP,count(sales_item) as chair where Sales_Item = 'chair'
it is working now how to add Laptop (3rd Column ) . can you please suggest
You would use conditional aggregation:
Select EmpId,
sum(case when sales_item = 'chair' then 1 else 0 end) as chairs,
sum(case when sales_item = 'laptop' then 1 else 0 end) as laptops
from EMP
group by EmpId;
There is no reason to store this in a separate table. If you like, you can create a view. Then when you access the view, you know the data is up-to-date.
You could use pivot for the expected result:
DECLARE #t TABLE(
EmpID varchar(3)
,SaleItems varchar(10)
,Paid varchar(20)
)
INSERT INTO #t VALUES
('ABC', 'chair', 'Yes')
,('WXY', 'chair', 'Under Review')
,('PER', 'Laptop', 'Yes')
,('ABC', 'Chair', 'Yes')
SELECT piv.EmpID, ISNULL(piv.chair, 0) AS chair, ISNULL(piv.Laptop, 0) AS Laptop
FROM(
SELECT EmpID, SaleItems, 1 cnt
FROM #t
) x
PIVOT
(
SUM(cnt)
FOR SaleItems IN ([chair], [Laptop])
) piv

SQL | View Column as Multiple Columns Based on Conditions

Newbie Postgresql (9.6.6) question here :)
I want to create a View that will split a single column into several columns, based on different conditions.
Example Table
Name Score Season
------- ------- --------
John 12 Fall
John 15 Winter
John 13 Spring
Sally 17 Fall
Sally 10 Winter
Sally 14 Spring
Henry 16 Fall
Henry 12 Winter
Henry 18 Spring
I want the View to dislay something that looks like this:
Name Fall Score Winter Score Spring Score
------- ------------ -------------- --------------
John 12 15 13
Sally 17 10 14
Henry 16 12 18
Where the "Score" field is broken out into several different columns, each one populated based on WHERE clause that references the "Season" field. I've looked into both Window Functions and CASE Statements for accomplishing this purpose, but haven't been successfully thus far.
Any help is greatly appreciated!
Selecting from the entire table while grouping over the Name and then conditionally SUMming over the Score column will work:
SELECT
"Name",
SUM(CASE WHEN "Season" = 'Fall' THEN "Score" ELSE 0 END) AS "Fall",
SUM(CASE WHEN "Season" = 'Winter' THEN "Score" ELSE 0 END) AS "Winter",
SUM(CASE WHEN "Season" = 'Spring' THEN "Score" ELSE 0 END) AS "Spring"
FROM "mytable"
GROUP BY "Name"
Whether or not you use SUM() is up to you and how your data looks. If you have one row per (Name, Season) pair then SUM() will work equally as well as MAX()
You need a pivot table:
On SQL server you can do something like this example (hope it's the same for postgress), in others versions of SQL exist the pivot relational operators, but I'm not sure if Pivot works on Postgres
Example:
CREATE TABLE #Table
(
Name nvarchar(400),
Score int,
Season nvarchar(400)
)
insert into #Table values ( 'John ',12,'Fall')
insert into #Table values ( 'John ',15,'Winter' )
insert into #Table values ( 'John ',13,'Spring' )
insert into #Table values ( 'Sally',17,'Fall ' )
insert into #Table values ( 'Sally',10,'Winter' )
insert into #Table values ( 'Sally',14,'Spring' )
insert into #Table values ( 'Henry',16,'Fall' )
insert into #Table values ( 'Henry',12,'Winter' )
insert into #Table values ( 'Henry',18,'Spring' )
select
c.Name
,sum(c.[Fall Score]) as [Fall Score]
,sum(c.[Winter Score]) as [Winter Score]
,sum(c.[Spring Score]) as [Spring Score]
from
(SELECT
t.name,
case
when t.Season = 'Fall' then t.Score
when t.Season = 'Winter' then 0
when t.Season = 'Spring' then 0
end as [Fall Score],
case
when t.Season = 'Fall' then 0
when t.Season = 'Winter' then t.Score
when t.Season = 'Spring' then 0
end as [Winter Score],
case
when t.Season = 'Fall' then 0
when t.Season = 'Winter' then 0
when t.Season = 'Spring' then t.Score
end as [Spring Score]
from #Table t
)as c
group by c.name

Excluding records within an aggregate function based on presence of value in another table

I'm writing a query that generates statistics based on postcodes and I need to be able to count the number of matching records that are within a range of postcodes except when they exist in a secondary table. This is part of a larger query and I need the count of records for each postcodes in columnar format rather than as separate rows and this minimal example demonstrates what I've attempted:
CREATE TABLE #People
(
Name nvarchar(10),
Postcode int
)
INSERT INTO #People VALUES ('Adam', 2000)
INSERT INTO #People VALUES ('John', 2001)
INSERT INTO #People VALUES ('Paul', 2001)
INSERT INTO #People VALUES ('Peter', 2099)
INSERT INTO #People VALUES ('Tom', 4000)
CREATE TABLE #PostcodesToIgnore
(
Postcode int
)
INSERT INTO #PostcodesToIgnore VALUES (2099)
SELECT SUM(CASE WHEN PostCode BETWEEN 2000 AND 2099 THEN 1 ELSE 0 END) FROM #People
SELECT SUM(CASE WHEN PostCode BETWEEN 2000 AND 2099
AND PostCode NOT IN (SELECT PostCode FROM #PostcodesToIgnore) THEN 1 ELSE 0 END)
FROM #People
The first query that counts all postcodes within the range works but the second one fails with the error:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
While I could refactor the query to include all the criteria from the outer select into each subselect there are quite a few criteria in the real query so I was hoping there might be a more elegant way to go about it?
You could use a left join instead.
SELECT
SUM
(
CASE WHEN PostCode BETWEEN 2000 AND 2099
AND pcti.PostCode is null
THEN 1
ELSE 0
END
)
FROM #People p
left join #PostcodesToIgnore pcti on pcti.PostCode = p.PostCode
You could remove the SUM and push the query into a derived table or CTE.
The following works
SELECT SUM(PostCodeFlag)
FROM (SELECT CASE
WHEN PostCode BETWEEN 2000 AND 2099
AND PostCode NOT IN (SELECT PostCode
FROM #PostcodesToIgnore) THEN 1
ELSE 0
END AS PostCodeFlag
FROM #People) T
Something like this:
Use a CTE to pre-prepare your data, then do a simple grouped count.
Or you could have a look on OVER (https://msdn.microsoft.com/en-us/library/ms189461.aspx)
WITH myCTE AS
(
SELECT Name,Postcode FROM #People
WHERE Postcode NOT IN (SELECT Postcode FROM #PostcodesToIgnore)
)
SELECT Postcode, Count(Name)
FROM myCTE
GROUP BY Postcode
FROM #people WHERE postcode not in (...).
In fact, it looks like you just don't need any CASE at all and you can specify all of your predicates in the FROM.
Or am I missing something ?

SQL Pivot Grid (Rows to Column

Hi im new to pivot tables and i hope you can help me out. I have a table called Additional User Info... It would be easier to show rather than explain:
Source:
T_Info
(ID No, Field, Value)
1000, Gender, Male
1000, Age, 18
1000, School, MIT
Result That I Want
ID, Gender, Age, School
1000, Male, 18, MIT
is this possible without using a cursor?
SELECT IDNo,
MAX(CASE WHEN field = 'Gender' THEN Value END) Gender,
MAX(CASE WHEN field = 'Age' THEN Value END) Age,
MAX(CASE WHEN field = 'School' THEN Value END) School
FROM mytable
GROUP BY IDNo