Query to both group by a name in one column and show total for each grouped element - sql

I have a table that looks something like this generic example.
+-------+--------------+------------+---------------+-----------+----------+
| id | name | amount | payment_type | year | period |
+-------+--------------+------------+---------------+-----------+----------+
| 1 | Bob | 20 | BACS | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
| 2 | Jim | 40 | BACS | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
| 3 | Bob | 60 | BACS | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
| 4 | Jim | 20 | Voucher | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
| 5 | Bob | 40 | BACS | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
| 6 | Bob | 20 | Cheque | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
| 7 | Jim | 20 | BACS | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
| 8 | Bob | 20 | BACS | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
| 9 | Bob | 20 | BACS | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
| 10 | Dan | 20 | BACS | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
| 11 | Dan | 20 | Voucher | 2019 | 5 |
+-------+--------------+------------+---------------+-----------+----------+
etc...
I would like to total up results of this table (Many more rows) and have a count of the total number of rows for each user, the total of the amount column for each user, and a total of each payment type for each user, narrowed down by year and period each time I run this query. (The year and period are user-select-able on a form.) So that I end up with a result set that looks something like this:
+-------+--------------+------------+---------------+-----------+----------+
| name | Total | BACS | Cheque | Voucher | Cost |
+-------+--------------+------------+---------------+-----------+----------+
| Bob | 6 | 5 | 1 | 0 | 180 |
+-------+--------------+------------+---------------+-----------+----------+
| Jim | 3 | 2 | 0 | 1 | 80 |
+-------+--------------+------------+---------------+-----------+----------+
| Dan | 2 | 1 | 0 | 1 | 40 |
+-------+--------------+------------+---------------+-----------+----------+
So I need a pivoting query of some kind using a sum case something like this:
SELECT
name,
SUM(CASE WHEN YEAR = '$safefy' THEN 1 ELSE 0 END) AS total,
SUM(CASE WHEN payment_type = 'BACs' OR payment_type = 'BACS' THEN 1 ELSE 0 END) AS BACS,
SUM(CASE WHEN payment_type = 'Cheque' THEN 1 ELSE 0 END) AS Cheque,
SUM(CASE WHEN payment_type = 'Voucher' THEN 1 ELSE 0 END) AS Voucher,
<Not sure what goes here.> AS Cost,
FROM
table_name
WHERE
YEAR = '2019' AND period = '5'
GROUP BY
name "
But I'm not sure how to total up the cost from the tables amount column for each user. I'm probably not using the correct terms in my search for answers because I don't know what it's called?

I think you just want sum(amount):
SELECT name,
SUM(CASE WHEN YEAR = '$safefy' THEN 1 ELSE 0 END) AS total,
SUM(CASE WHEN payment_type = 'BACs' OR payment_type = 'BACS' THEN 1 ELSE 0 END) AS BACS,
SUM(CASE WHEN payment_type = 'Cheque' THEN 1 ELSE 0 END) AS Cheque,
SUM(CASE WHEN payment_type = 'Voucher' THEN 1 ELSE 0 END) AS Voucher,
SUM(amount) AS Cost
FROM table_name
WHERE YEAR = '2019' AND period = '5'
GROUP BY name;
You can simplify the first part because the CASE is not needed:
SELECT name,
SUM(YEAR = '$safefy') AS total,
SUM(payment_type IN ('BACs', 'BACS') THEN 1 ELSE 0 END) AS BACS,
SUM(payment_type = 'Cheque') AS Cheque,
SUM(payment_type = 'Voucher') AS Voucher,
SUM(amount) AS Cost
FROM table_name
WHERE YEAR = '2019' AND period = '5'
GROUP BY name

Related

Combining fields with GROUP BY ROLLUP

I want to have a query that shows every contract made in my company and have it grouped by function.
My problem is that I have a lot of functions, for instance:
Partner
Seller
Manager
Captain
Etc.
I want to show 3 rows, one with all contracts from "Partners", other by all the "Interns" (everyone that isn't a partner), and last one with a Total of the two, achieved with the ROLLUP. But I can't seem to only show this 3 rows, my query turns everything that is not partner to be name "intern", but doesn't combine this cells, so I get this result:
+--------------+-------------+--------------+---------+
| Seller Type | Installed | Scheduled | Total |
+--------------+-------------+--------------+---------+
| Intern | 1 | 0 | 1 |
+--------------+-------------+--------------+---------+
| Intern | 3 | 0 | 3 |
+--------------+-------------+--------------+---------+
| Partner | 10 | 5 | 15 |
+--------------+-------------+--------------+---------+
| Intern | 19 | 10 | 29 |
+--------------+-------------+--------------+---------+
| Total | 33 | 15 | 48 |
+--------------+-------------+--------------+---------+
And my goal was to Group everyone that isn't a "partner" into the same row, and show it like this:
+--------------+-------------+--------------+---------+
| Seller Type | Installed | Scheduled | Total |
+--------------+-------------+--------------+---------+
| Partner | 10 | 5 | 15 |
+--------------+-------------+--------------+---------+
| Intern | 23 | 10 | 33 |
+--------------+-------------+--------------+---------+
| Total | 33 | 15 | 48 |
+--------------+-------------+--------------+---------+
My data for table CM3
+--------------+-------------+--------------+
| u_func | cm | name |
+--------------+-------------+--------------+
| Captain | 1 | Chris |
+--------------+-------------+--------------+
| Manager | 2 | Mary |
+--------------+-------------+--------------+
| Partner | 3 | Anthony |
+--------------+-------------+--------------+
| Seller | 4 | Hannah |
+--------------+-------------+--------------+
My data for table BO
+--------------+-------------+--------------+
| stamp | seller | sta |
+--------------+-------------+--------------+
| PO109832910 | 1 | Installed |
+--------------+-------------+--------------+
| PO389213201 | 2 | Installed |
+--------------+-------------+--------------+
| PO930821639 | 3 | Scheduled |
+--------------+-------------+--------------+
| PO987583213 | 4 | Scheduled |
+--------------+-------------+--------------+
My query is
SELECT CASE
WHEN GROUPING(CM3.u_func)=1 THEN 'TOTAL'
WHEN CM3.u_func <> 'Partner' THEN 'Intern'
END "Seller Type"
, COUNT(CASE
WHEN BO.stat = 'Installed' THEN 1
END) "Installed"
, COUNT(CASE
WHEN BO.stat = 'Scheduled' THEN 1
END) "Scheduled"
, COUNT(CASE
WHEN BO.stat IN ('Installed','Scheduled') THEN 1
END) "Total"
FROM BO
JOIN CM3 ON cm3.cm = BO.seller
GROUP BY ROLLUP(CM3.u_func)
You need your GROUP BY to mirror the logic in your SELECT statement (which also needs some adjustment):
SELECT CASE
WHEN GROUPING
(CASE
WHEN CM3.u_func = 'Partner' THEN CM3.u_func
ELSE 'Intern'
END) = 1 THEN 'TOTAL'
ELSE CASE
WHEN CM3.u_func = 'Partner' THEN CM3.u_func
ELSE 'Intern'
END
END "Seller Type"
, COUNT(CASE
WHEN BO.stat = 'Installed' THEN 1
END) "Installed"
, COUNT(CASE
WHEN BO.stat = 'Scheduled' THEN 1
END) "Scheduled"
, COUNT(CASE
WHEN BO.stat IN ('Installed','Scheduled') THEN 1
END) "Total"
FROM BO
JOIN CM3 ON cm3.cm = BO.seller
GROUP BY CASE
WHEN CM3.u_func = 'Partner' THEN CM3.u_func ELSE 'Intern'
END WITH ROLLUP

How should I combine multiple columns and convert to rows?

I'm working on a table like the one below, and the table name is information:
-------------------------------
|id | gender | country | age |
-------------------------------
| 1 | F | America | 18 |
-------------------------------
| 2 | M | America | 22 |
-------------------------------
| 3 | M | Japan | 30 |
-------------------------------
| 4 | M | Brazil | 32 |
-------------------------------
| 5 | F | Norway | 34 |
-------------------------------
| 7 | M | America | 32 |
-------------------------------
|10 | F | Norway | 22 |
-------------------------------
|13 | F | Japan | 36 |
-------------------------------
|14 | F | Brazil | 19 |
-------------------------------
|25 | F | Japan | 33 |
-------------------------------
And I want the output to be:
------------------------------------------------------
| country | total | Female | Male | 18 - 25 | 26 -35 |
------------------------------------------------------
| America | 3 | 1 | 2 | 2 | 1 |
------------------------------------------------------
| Japan | 3 | 2 | 1 | 0 | 3 |
------------------------------------------------------
| Brazil | 2 | 1 | 1 | 1 | 1 |
------------------------------------------------------
| Norway | 2 | 2 | 0 | 1 | 1 |
------------------------------------------------------
The following is my approach, first count the distinct id group by country and gender:
SELECT country, gender, COUNT(DISTINCT id) FROM information GROUP BY 1,2;
Then try to create the table with age range:
(SELECT '18-25' AS '18-25'
SUM(CASE WHEN (AGE >=18 AND AGE<=25) THEN 1 ELSE 0 END) AS NUM FROM information)
UNION
(SELECT '26-35' AS '18-25'
SUM(CASE WHEN (AGE >=26 AND AGE<=35) THEN 1 ELSE 0 END) AS NUM FROM information)
But I'm not sure how to combine them and covert the columns to rows. Could anyone give me some suggestion?
Use conditional aggregation:
select country, count(*) as total,
sum(case when gender = 'F' then 1 else 0 end) as num_female,
sum(case when gender = 'M' then 1 else 0 end) as num_male,
sum(case when age >= 18 and age <= 25 then 1 else 0 end) as age_18_25,
sum(case when age >= 26 and age <= 35 then 1 else 0 end) as age_26_35
from information
group by country
A slight variation on Gordon's answer which I prefer, because it eliminates the need for an explicit ELSE case:
SELECT
country,
COUNT(*) AS total,
COUNT(CASE WHEN gender = 'F' THEN 1 END) AS Female,
COUNT(CASE WHEN gender = 'M' THEN 1 END) AS Male,
COUNT(CASE WHEN age BETWEEN 18 AND 25 THEN 1 END) AS "18-25",
COUNT(CASE WHEN age BETWEEN 26 AND 35 THEN 1 END) AS "26-35"
FROM information
GROUP BY
country;
Less code = usually a better thing

SQL Group by With Case

Name | Amount | Date
_____________________
Jacob | 15 | 1-Oct-16
Jacob | 50 | 2-Oct-16
Ethan | 24 | 3-Oct-16
Jacob | 30 | 3-Oct-16
William | 17 | 1-Oct-16
William | 11 | 2-Oct-16
Ethan | 36 | 3-Oct-16
Ethan | 9 | 1-Oct-16
I have above table, and i want Sum of amount for last 5 days something like
Name | 5-oct-16 | 4-oct-16 | 3-oct-16 | 2-oct-16| 1-oct-16
___________________________________________________________
Jacob | 0 | 0 |30 | 50 | 15
Ethan | 0 | 0 |60 | 0 | 9
William | 0 | 0 |0 | 11 | 17
want to group by on Name with amount sum of last 5 days separated.
I think you want conditional aggregation:
SELECT Name,
SUM(CASE DATE WHEN '2016-10-05' THEN Amount ELSE 0 END) as amount_2016105,
SUM(CASE DATE WHEN '2016-10-04' THEN Amount ELSE 0 END) as amount_2016104,
SUM(CASE DATE WHEN '2016-10-03' THEN Amount ELSE 0 END) as amount_2016103,
SUM(CASE DATE WHEN '2016-10-02' THEN Amount ELSE 0 END) as amount_2016102,
SUM(CASE DATE WHEN '2016-10-01' THEN Amount ELSE 0 END) as amount_2016101
FROM MyTable
GROUP BY Name;

Calculation going wrong due to JOIN issue

Table -
+----+-----------+-----------+---------+---------------------+------------+
| ID | Client_Id | Driver_Id | City_Id | Status | Request_at |
+----+-----------+-----------+---------+---------------------+------------+
| 1 | 1 | 10 | 1 | completed | 2013-10-01 |
| 2 | 2 | 11 | 1 | cancelled_by_driver | 2013-10-01 |
| 3 | 3 | 12 | 6 | completed | 2013-10-01 |
| 4 | 4 | 13 | 6 | cancelled_by_client | 2013-10-01 |
| 5 | 1 | 10 | 1 | completed | 2013-10-02 |
| 6 | 2 | 11 | 6 | completed | 2013-10-02 |
| 7 | 3 | 12 | 6 | completed | 2013-10-02 |
| 8 | 2 | 12 | 12 | completed | 2013-10-03 |
| 9 | 3 | 10 | 12 | completed | 2013-10-03 |
| 10 | 4 | 13 | 12 | cancelled_by_driver | 2013-10-03 |
+----+-----------+-----------+---------+---------------------+------------+
My attempt -
WITH src
AS (SELECT Count(status) AS Denom,
request_at
FROM trips
WHERE status = 'completed'
GROUP BY request_at),
src2
AS (SELECT Count(status) AS Num,
request_at
FROM trips
WHERE status <> 'completed'
GROUP BY request_at)
SELECT Cast(Count(num) AS FLOAT)/Cast(Count(Denom) AS FLOAT) AS cancel_rate,
trips.request_at
FROM src,
src2,
trips
GROUP BY trips.request_at;
I am trying to find the cancellation rate per day but it is clearing wrong (MY OUTPUT)-
+-------------+------------+
| cancel_rate | request_at |
+-------------+------------+
| 24 | 2013-10-01 |
| 18 | 2013-10-02 |
| 18 | 2013-10-03 |
+-------------+------------+
The cancellation rate for 2013-10-01 should be 0.5 and not 24. Similarly for other dates it should be different.
I know the problem lies with this part but I do not know what is the correct way or how to approach it
SELECT Cast(Count(num) AS FLOAT)/Cast(Count(Denom) AS FLOAT) AS cancel_rate,
trips.request_at
FROM src,
src2,
trips
Is there any way to put in more than 1 select statement in With NAME as () clause ? So that I won't use any JOIN or multiple tables.
Use conditional aggregation:
SELECT SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as denom,
SUM(CASE WHEN status <> 'completed' THEN 1 ELSE 0 END) as num,
AVG(CASE WHEN status <> 'completed' THEN 1.0 ELSE 0 END) as cancel_rate
FROM trips
GROUP BY request_at;
Note that calculation for the cancel_rate. This is simpler to do using AVG() rather than dividing the two values. The use of 1.0is because SQL Server does integer arithmetic, so 1 / 2 is 0 rather than 0.5.
OK, a bit late again, but here is another variation (edited):
SELECT SUM(CASE LEFT(status,9) WHEN 'cancelled' THEN 1. ELSE 0 END)
/COUNT(*) cancellation_rate,
request_at
FROM trips GROUP BY request_at ORDER BY request_at

SQL query select with ranges

I have one table like this...
+---------+------------+------+-------+
| City | NamePeople | Age | Money |
+---------+------------+------+-------+
| Paris | John | 10 | 200 |
| London | Marie | 21 | 300 |
| Paris | Kevin | 50 | 250 |
| Paris | Julia | 45 | 150 |
+---------+------------+------+-------+
I need a result with ranges , something like this
+---------------+------------+-----------------------+-------------------------+
| City | Sum(Money) | CountPeopleWithAge<30 | CountPeopleWithAge>30 |
+---------------+------------+-----------------------+-------------------------+
| Paris | 600 | 1 | 2 |
| London | 300 | 1 | 0 |
+---------------+------------+-----------------------+-------------------------+
How do I do this with sql select?
Thanks.
Select
City,
Sum(Money),
Sum(Case When Age < 30 Then 1 Else 0 End),
Sum(Case When Age > 30 Then 1 Else 0 End)
From
table
Group By
City
You can use this Query,
SELECT City,
SUM(Money),
SUM(case when Age < 30 then 1 else 0 end),
SUM(case when Age > 30 then 1 else 0 end)
FROM tableA
GROUP BY City