SQL count products - sql

I have table:
name product
john beer
john milk
john tea
john beer
emily milk
emily milk
emily tea
john beer
i need select from this table, when output will be:
name count(tea) count(beer) count(milk) count(total)
john 1 3 1 5
emily 1 0 2 3
any idea how to do this?
DB: oracle 12

Use conditional aggregation:
select name
sum(case when product = 'tea' then 1 else 0 end) cnt_tea,
sum(case when product = 'beer' then 1 else 0 end) cnt_beer,
sum(case when product = 'milk' then 1 else 0 end) cnt_milk,
count(*) total
from mytable
group by name
Depending on your database, there may be neater options available to express the conditional counts.

Related

sql select and replace

I have a query that will show all the client information, and there is a column called sex, which 1 is female, 0 is male. How can I do another select on this result, that turn all the female record, turn the 1 to 100, male to 101? ( 1 and 0 in the first query, they are bit, in the second they are nvarchar) (Mssql)
select * from tblClientInfo
001 Derrick 0
002 Mary 1
then, turn it to
001 Derrick 100
002 Mary 101
You can use case:
select (case when male = 0 then '100' else '101' end)

Transact-SQL Select Query Giving duplicate values

first off, thank you for taking the time to look at this.
I am trying to collect data from 3 tables and make a reference chart that allows the end user to see the stored data.
Basically I have 3 tables for this example:
USERS:
USER_PK USER_ID USER_NAME
1 10000 Bob
2 10001 Sally
3 10003 Joe
4 10004 Susan
SKILL_TYPE:
SKILL_PK SKILL_NAME
11 Point of Sale
22 Digital Sales
33 Customer Service
44 Specialist Support
SKILL_ASSOCIATION:
SKILL_ASSOC_PK SKILL_PK USER_PK START_DATE STOP_DATE Priority
99 11 1 36526 500000 2
88 11 2 36527 500000 3
77 22 1 36526 500000 3
66 33 3 36528 500000 1
55 44 4 36525 500000 1
444 33 4 36525 500000 4
(I know I've probably broken some rules with cataloging this data I did it in SQL Express, however it is only an example and not representative of the real data I am using)
My Select Query Returns an unwanted result with multiple lines for each USER:
Statement:
SELECT USERS.[USER_NAME], USERS.[USER_ID],
(CASE WHEN ST.SKILL_NAME ='Point of Sale' Then SA.[PRIORITY] END) AS 'POS',
(CASE WHEN ST.SKILL_NAME ='Digital Sales' Then SA.[PRIORITY] END) AS 'DS',
(CASE WHEN ST.SKILL_NAME ='Customer Service' Then SA.[PRIORITY] END) AS 'CS',
(CASE WHEN ST.SKILL_NAME ='Specialist Support' Then SA.[PRIORITY] END) AS 'Spec'
FROM USERS
INNER JOIN [dbo].[SKILL_ASSOCIATION] AS SA ON SA.USER_PK = USERS.USER_PK
INNER JOIN SKILL_TYPE AS ST ON ST.SKILL_PK = SA.SKILL_PK
Result:
USER_NAME USER_ID POS DS CS Spec
Bob 10000 2 NULL NULL NULL
Sally 10001 3 NULL NULL NULL
Bob 10000 NULL 3 NULL NULL
Joe 10003 NULL NULL 1 NULL
Susan 10004 NULL NULL NULL 1
Susan 10004 NULL NULL 4 NULL
I've tried using distinct as well with similar results.
Desired Results:
NAME ID POS DS CS Spec
Bob 1 2 3
Sally 2 3
Joe 1
Susan 4 1
I have very limited Query access with this SQL Server and cannot create/modify or delete from it to accomplish my objective.
Any guidance would be much appreciated!
Thanks,
Steven
Your expected output implies that an aggregation by user along with taking the MAX of each of the CASE expressions should work:
SELECT
u.[USER_NAME],
u.[USER_ID],
MAX(CASE WHEN ST.SKILL_NAME = 'Point of Sale' THEN SA.[PRIORITY] END) AS POS,
MAX(CASE WHEN ST.SKILL_NAME = 'Digital Sales' THEN SA.[PRIORITY] END) AS DS,
MAX(CASE WHEN ST.SKILL_NAME = 'Customer Service' THEN SA.[PRIORITY] END) AS CS,
MAX(CASE WHEN ST.SKILL_NAME = 'Specialist Support' THEN SA.[PRIORITY] END) AS Spec
FROM USERS u
INNER JOIN [dbo].[SKILL_ASSOCIATION] AS SA
ON SA.USER_PK = u.USER_PK
INNER JOIN SKILL_TYPE AS ST
ON ST.SKILL_PK = SA.SKILL_PK
GROUP BY
u.[USER_NAME],
u.[USER_ID];

simple sql over (partition by) not working as expected

Feels like it should be simple but my mind has gone blank so would appreciate any help!
Let's say I have this dataset
Date sale_id salesperson Missed_payment_this_month
01/01/2016 1001 John 1
01/01/2016 1002 Bob 0
01/01/2016 1003 Bob 0
01/01/2016 1004 John N/A
01/02/2016 1001 John 1
01/02/2016 1002 Bob 1
01/02/2016 1003 Bob 0
01/02/2016 1004 John 1
01/03/2016 1001 John 1
01/03/2016 1002 Bob 0
01/03/2016 1003 Bob 0
01/03/2016 1004 John 1
And want to add these two columns to the end. They look at the number of missed payments previously, by sales_id and salesperson.
Previous_missed_payment_by_sale_id Previous_missed_payment_by_sales person
0 0
0 0
0 0
0 0
1 1
0 0
0 0
0 1
2 3
1 1
0 1
1 3
sales_id is ok but getting it over sales persons is giving me an error (group by) or adding in extra columns. I need to keep the rows constant.
My best guess that returns extra columns:
select t1.Date, t1.sale_id, t1.salesperson
,sum(case when t2.Missed_payment_this_month = '1' then 1 else 0 end) previous_missed_sales_id
,sum(case when t2.Missed_payment_this_month = '1' then 1 else 0 end) OVER (PARTITION by t1.salesperson) previous_missed_salesperson
from [dbo].[simple_join_table2] t1
inner join [dbo].[simple_join_table2] t2 on
(t2.[Date] < t1.[Date] AND t1.[sale_id] = t2.[sale_id])
group by t1.Date, t1.sale_id, t1.salesperson
,case when t2.Missed_payment_this_month = '1' then 1 else 0 end
this is the output:
Date sale_id salesperson previous_missed_sales_id previous_missed_salesperson
01/02/2016 1002 Bob 0 1
01/02/2016 1003 Bob 0 1
01/03/2016 1002 Bob 0 1
01/03/2016 1002 Bob 1 1
01/03/2016 1003 Bob 0 1
01/02/2016 1001 John 1 3
01/02/2016 1004 John 0 3
01/03/2016 1001 John 2 3
01/03/2016 1004 John 0 3
01/03/2016 1004 John 1 3
Is this possible without another sub query? I guess another way to put it is i'm trying to mimic the sumx and earlier functions of Powerpivot.
If you are on 2012+ use windowing aggregates. Previous = sum all_previous_including_curret - sum current. Ms sql default window is exactly ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
with [simple_join_table2] as(
-- sample data
select cast(valuesDate as Date) valuesDate, sale_id, salesperson, Missed_payment_this_month
from (
values
('20160101',1001,'John', 1)
,('20160101',1002,'Bob ', 0)
,('20160101',1003,'Bob ', 0)
,('20160101',1004,'John',null)
,('20160201',1001,'John', 1)
,('20160201',1002,'Bob ', 1)
,('20160201',1003,'Bob ', 0)
,('20160201',1004,'John', 1)
,('20160301',1001,'John', 1)
,('20160301',1002,'Bob ', 0)
,('20160301',1003,'Bob ', 0)
,('20160301',1004,'John', 1)
) t(valuesDate, sale_id, salesperson, Missed_payment_this_month)
)
select valuesDate,sale_id, salesperson, Missed_payment_this_month,
byidprevmonth = sum(Missed_payment_this_month ) over(partition by sale_id order by valuesDate)
- sum(Missed_payment_this_month) over(partition by valuesDate, sale_id),
bypersonprevmonth = sum(Missed_payment_this_month) over(partition by salesperson order by valuesDate)
- sum(Missed_payment_this_month) over(partition by valuesDate, salesperson)
from [simple_join_table2]
order by salesperson, valuesDate

Getting the sum of columns based on row values

I have a table that looks like the following.
EMPNUM EMPNAME LOCATION CATEGORY COUNT
123 JOHN DOE BLDG A 1 5
123 JOHN DOE BLDG A 1 6
123 JOHN DOE BLDG A 2 4
123 JOHN DOE BLDG A 3 7
123 JOHN DOE BLDG B 1 1
123 JOHN DOE BLDG B 2 3
234 EMILY DOE BLDG A 1 1
234 EMILY DOE BLDG A 2 2
234 EMILY DOE BLDG A 3 4
234 EMILY DOE BLDG B 2 3
234 EMILY DOE BLDG B 2 9
234 EMILY DOE BLDG B 3 3
I would like to transport it into columns that will yield to an output similar to below. I need to get the sum of COUNT based on the values of LOCATION and CATEGORY
EMPNUM EMPNAME SUM_A1 SUM_A2 SUM_A3 SUM_B1 SUM_B2 SUM_B3
123 JOHN DOE 11 4 7 1 3 0
234 EMILY DOE 1 2 4 0 12 3
Is there any way to do this as an SQL query? or in Crystal reports (though I prefer output using SQL)
If you are using 11g or later try
select * from table1
PIVOT (SUM("COUNT")
FOR ("LOCATION","CATEGORY") IN
(('BLDG A',1) AS sum_a1,
('BLDG A',2) AS sum_a2,
('BLDG A',3) AS sum_a3,
('BLDG B',1) AS sum_b1,
('BLDG B',2) AS sum_b2,
('BLDG B',3) AS sum_b3));
Here is a fiddle
Otherwise use APC's solution
This will work providing the values in LOCATION and CATEGORY are constant:
select empnum
, empname
, sum(case when location='BLDG A' and category = 1 then count else 0 end) sum_a1
, sum(case when location='BLDG A' and category = 2 then count else 0 end) sum_a2
, sum(case when location='BLDG A' and category = 3 then count else 0 end) sum_a3
, sum(case when location='BLDG B' and category = 1 then count else 0 end) sum_b1
, sum(case when location='BLDG B' and category = 2 then count else 0 end) sum_b2
, sum(case when location='BLDG B' and category = 3 then count else 0 end) sum_b3
from your_table
group by empnum
, empname
If the values are not known or not stable when you run the query you will need to use dynamic SQL.
Note that if you are on 11g you should employ A B Cade's PIVOT solution, which is more elegant.
The other answers will work great if you have a known number of values to transform into columns. But if you have an unknown number, then you can use dynamic sql to generate the results.
You would create the following procedure:
CREATE OR REPLACE procedure test_dynamic_pivot(p_cursor in out sys_refcursor)
as
sql_query varchar2(1000) := 'select empnum, empname';
begin
for x in (select distinct location, category from yourtable order by 1)
loop
sql_query := sql_query ||
' , sum(case when location = '''||x.location||''' and category='||x.category||' then cnt else 0 end) as sum_'||substr(x.location, -1, 1)||x.category;
dbms_output.put_line(sql_query);
end loop;
sql_query := sql_query || ' from yourtable group by empnum, empname';
open p_cursor for sql_query;
end;
/
And then to execute it:
variable x refcursor
exec test_dynamic_pivot(:x)
print x
The result is the same as the hard-coded version:
| EMPNUM | EMPNAME | SUM_A1 | SUM_A2 | SUM_A3 | SUM_B1 | SUM_B2 | SUM_B3 |
----------------------------------------------------------------------------
| 234 | EMILY DOE | 1 | 2 | 4 | 0 | 12 | 3 |
| 123 | JOHN DOE | 11 | 4 | 7 | 1 | 3 | 0 |

SQL query to pivot a column using CASE WHEN

I have the following table:
Bank:
name val amount
John 1 2000
Peter 1 1999
Peter 2 1854
John 2 1888
I am trying to write an SQL query to give the following result:
name amountVal1 amountVal2
John 2000 1888
Peter 1999 1854
So far I have this:
SELECT name,
CASE WHEN val = 1 THEN amount ELSE 0 END AS amountVal1,
CASE WHEN val = 2 THEN amount ELSE 0 END AS amountVal2
FROM bank
However, it gives the slightly wrong result:
name amountVal1 amountVal2
John 2000 0
Peter 1999 0
John 0 1888
Peter 0 1854
How can I modify my query to give the correct presentation?
Thanks
SELECT
name,
SUM(CASE WHEN val = 1 THEN amount ELSE 0 END) AS amountVal1,
SUM(CASE WHEN val = 2 THEN amount ELSE 0 END) AS amountVal2
FROM bank GROUP BY name
Looks like you need to join the table on itself. Try this:
select bank1.name, bank1.amount, bank2.amount
from bank bank1
inner join bank bank2 on
bank1.name = bank2.name
and bank1.val = 1
and bank2.val = 2