SQL query select with ranges - sql

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

Related

SQL - Need return from single table of SUMs over 3 different date ranges

I have a table of Account Transactions that includes ID, Amount, Date. Basically, I want to create a resulting table that looks at the table and returns what the SUM was for the Account over three different Ending Date Ranges. Then I want to Flag (Combined_Flag) each Account ID, 1 if any of the SUMs for that ID are non-zero, and a 0 if all of the SUMs are 0.
Date Range 1) Min Date to End of Last Month (-1 Month)
Date Range 2) Min Date to End of 2 Months ago (-2 Months)
Date Range 3) Min Date to End of Last Month, Last Year (-13 Months)
The Resulting table should be: ID, SUM_R1, SUM_R2, SUM_R3, Flag_R1, Flag_R2, Flag_R3, Combined_Flag
Example Data
| ID | Amount | Date |
| -------- | -------------- |-------------- |
| 1 | 20 | 09/01/19 |
| 2 | 40 | 09/01/19 |
| 3 | 0 | 09/01/19 |
| 4 | 0 | 09/01/19 |
| 1 | 10 | 10/01/19 |
| 2 | 0 | 10/01/19 |
| 3 | 0 | 10/01/19 |
| 4 | 0 | 10/01/19 |
| 1 | 15 | 11/01/19 |
| 2 | 40 | 11/01/19 |
| 3 | 0 | 11/01/19 |
| 4 | 0 | 11/01/19 |
| 1 | 20 | 09/01/20 |
| 2 | 40 | 09/01/20 |
| 3 | 0 | 09/01/20 |
| 4 | 50 | 09/01/20 |
| 1 | 10 | 10/01/20 |
| 2 | 0 | 10/01/20 |
| 3 | 0 | 10/01/20 |
| 4 | 65 | 10/01/20 |
| 1 | 15 | 11/01/20 |
| 2 | 40 | 11/01/20 |
| 3 | 0 | 11/01/20 |
| 4 | 0 | 11/01/20 |
Expected Result Table (Using Date of 12/21/2020)
| ID | SUM_R1 | SUM_R2 | SUM_R3 | Flag_R1 | Flag_R2 | Flag_R3 | Combined_Flag |
| -------- | -------- | -------- | -------- | --------- | --------- | --------- | --------------- |
| 1 | 90 | 75 | 45 | 1 | 1 | 1 | 1 |
| 2 | 160 | 120 | 80 | 1 | 1 | 1 | 1 |
| 3 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 4 | 115 | 115 | 0 | 1 | 1 | 0 | 1 |
The difficulty I'm having is in joining the table basically to itself 2 times. I'm getting results all over the place and not really sure exactly what's going on.
Is this what you want?
select id,
sum(case when date < datefromparts(year(v.dte), month(v.dte), 1)
then amount else 0
end) as sum_r1,
sum(case when date < dateadd(month, -1, datefromparts(year(v.dte), month(v.dte), 1))
then amount else 0
end) as sum_r2,
sum(case when date < dateadd(month, -13, datefromparts(year(v.dte), month(v.dte), 1))
then amount else 0
end) as sum_r3,
max(case when amount > 0 and date < datefromparts(year(v.dte), month(v.dte), 1)
then 1 else 0
end) as flag_r1,
max(case when amount > 0 and date < dateadd(month, -1, datefromparts(year(v.dte), month(v.dte), 1))
then 1 else 0
end) as flag_r2,
max(case when amount > 0 and date < dateadd(month, -13, datefromparts(year(v.dte), month(v.dte), 1))
then 1 else 0
end) as flag_r3
from t cross join
(values (convert(date, '2020-12-21'))
) v(dte)
group by id;
The flag columns assume that the amounts are never negative (which is consistent with the data in your question.
EDIT:
The shorthand in the comment for creating the flag looks like:
abs(sign(sum(case when amount > 0 and date < datefromparts(year(v.dte), month(v.dte), 1)
then amount else 0
end))) as flag_r1,
Here is a db<>fiddle.

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

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

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

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;

SQL Renaming NULL for null values and rollup

I'm trying to make a dashboard to show open tickets and who they are assigned to, by priority. The data set looks like:
|-------------------------|
| Assigned | Priority |
|-------------------------|
| JOE | Low |
| JOE | Medium |
| MARY | High |
| | Medium |
| TIM | Low |
| Mary | High |
The report I'm trying to get is:
|---------------------------------------------------------|
| Employee | Low | Medium | High | Total |
|---------------------------------------------------------|
| Total | 2 | 2 | 2 | 6 |
| Unassigned | 0 | 1 | 0 | 1 |
| MARY | 0 | 0 | 2 | 2 |
| JOE | 1 | 1 | 0 | 2 |
| TIM | 1 | 0 | 0 | 1 |
I can get it all, with this query
SELECT
CASE WHEN Assigned is null then 'Unassigned' Else Assigned End Employee
, SUM(CASE WHEN Priority = 'Low' THEN 1 ELSE 0 END) AS Low
, SUM(CASE WHEN Priority = 'Medium' THEN 1 ELSE 0 END) AS Medium
, SUM(CASE WHEN Priority = 'High' THEN 1 ELSE 0 END) AS High
, count(Priority) AS Total
FROM table
GROUP BY Assigned WITH ROLLUP
ORDER BY Assigned ASC
This way however, it is renaming anything that was NULL to Unassigned. This is perfect to give me the Unassigned values that don't have anyone in the Assigned field, however my Total column is also showing Unassigned. How do I rename that to Total?
Thanks in advance!
You need to use GROUPING, like this:
SQL Fiddle
SELECT
CASE WHEN (GROUPING(Assigned) = 1) THEN 'Total'
When Assigned is null then 'Unassigned'
Else Assigned End Employee
, SUM(CASE WHEN Priority = 'Low' THEN 1 ELSE 0 END) AS Low
, SUM(CASE WHEN Priority = 'Medium' THEN 1 ELSE 0 END) AS Medium
, SUM(CASE WHEN Priority = 'High' THEN 1 ELSE 0 END) AS High
, count(Priority) AS Total
FROM table1
GROUP BY Assigned WITH ROLLUP
--DER BY Assigned ASC
Results:
| EMPLOYEE | LOW | MEDIUM | HIGH | TOTAL |
|------------|-----|--------|------|-------|
| Unassigned | 0 | 1 | 0 | 1 |
| JOE | 1 | 1 | 0 | 2 |
| MARY | 0 | 0 | 2 | 2 |
| TIM | 1 | 0 | 0 | 1 |
| Total | 2 | 2 | 2 | 6 |