SQL - Aggregate column based on filters - sql

My data has an ID, date, and points_earned field. Each row represents the points earned by the ID for that date.
I want to create something similar to this:
ID | Points_Earned_January | Points_Earned_February | Points_Earned_March
The result should be GROUP BY ID. Points_Earned_January should give a SUM of all the points_earned that fall in January for that ID.
How can I do this?

You can use conditional aggregation:
select id,
(case when date >= '2020-02-01' and date < '2020-03-01' then points else 0 end) as points_feb,
(case when date >= '2020-03-01' and date < '2020-04-01' then points else 0 end) as points_mar
from t
group by id;

Related

It is required to write a query that returns, for two dates, the total balances of active deals on these dates in SQL

The following table is given:
Columns
Deal start date
Deal end date
Amount
There is a table:
Start Date
End Date
Amount
01.01.2020
01.01.2021
1
15.04.2020
15.04.2021
3
It is required to write a query that returns, for two dates, the total balances of active deals on these dates.
It is required to display balances on date 1 = 02.05.2020 and date 2 = 02.02.2021
I know how to get them one by one
SELECT SUM(Amount)
FROM Table
WHERE 02.05.2020 >= End Date
Or
SELECT SUM(Amount)
FROM Table
WHERE 02.05.2020 >= End Date OR 02.02.2021 >= End Date
But I have no idea how to create a separate table for dates:
date 1 || amount 1
date 2 || amount 2
Can you give me some advice or direction for a solution?
You could use conditional aggregation or filtered sum function as the following:
SELECT
SUM(CASE WHEN '02.05.2020' >= End_Date THEN Amount ELSE 0 END) AS Date1,
SUM(CASE WHEN '02.05.2020' >= End_Date OR '02.02.2021' >= End_Date THEN Amount ELSE 0 END) AS Date2
FROM table_name
Or:
SELECT
SUM(Amount) FILTER (WHERE '02.05.2020' >= End_Date) AS Date1,
SUM(Amount) FILTER (WHERE '02.05.2020' >= End_Date OR '02.02.2021' >= End_Date) AS Date2
FROM table_name
See a demo.

Count on a column with a conditional statement in SQL?

I want a table as this:
cycle_end_date | count(user_id) | count(repaid_user_id) | event_ts | repayment_ts
15th Jan | 20 | 15 | 23rd Jan | 25th Jan
15th Jan | 30 | 30 | 24th Jan | 24th Jan
I am using the following query:
select
date(cycle_end_date) as cycle_end_date
,date(payload_event_timestamp + interval '5 hours 30 minutes') as event_date
,d.payment_date
,count( s.user_id) as count
,count(case s.user_id when d.payment_date < event_date then 1 else 0 end) as repaid_users
d.payment_date is taken from another query -
case when ots_created_at is null then
current_date else date(ots_created_at + interval ' 5 hours 30 minutes')
end as payment_date
this is giving me an error : ProgrammingError: operator does not exist: character varying = boolean
Basically I want the count of users who repaid on the day event was triggered. which is basically if event date is equal to repayment date we can find out that these many users paid on that event date. How to find this?
You have attempted to use a "short form" of case expression which starts like this:
case s.user_id when d.payment_date
For this format to be valid the syntax after when assumes an equal comparison (e.g. when d.payment_date assumes =) and in this short form it can only be an equality operator that is assumed. For more complex comparisons (less than, not equal, etc.) it is necessary to use the longer format of a case expression, like this:
case when d.payment_date < event_date then user_id end
Note that the aggregate function COUNT() increments for every non-null value, so to avoid counting any unwanted conditions do NOT return any value using else. e.g.
count(case when d.payment_date < event_date then user_id end) as col1
or, if you prefer to be more explicit in the query code, use else to return NULL which will not be counted, e.g.
count(case when d.payment_date < event_date then user_id else NULL end) as col1
nb: Instead of returning a column value you could use a constant like this:
count(case when d.payment_date < event_date then 1 end) as col1

How to define the filter in dates?

With the query, I basically want to compare avg_clicks at different time periods and set a filter according to the avg_clicks.
The below query gives us avg_clicks for each shop in January 2020. But I want to see the avg_clicks that is higher than 0 in January 2020.
Question 1: When I add the where avg_clicks > 0 in the query, I am getting the following error: Column 'avg_clicks' cannot be resolved. Where to put the filter?
SELECT AVG(a.clicks) AS avg_clicks,
a.shop_id,
b.shop_name
FROM
(SELECT SUM(clicks_on) AS clicks,
shop_id,
date
FROM X
WHERE site = ‘com’
AND date >= CAST('2020-01-01' AS date)
AND date <= CAST('2020-01-31' AS date)
GROUP BY shop_id, date) as a
JOIN Y as b
ON a.shop_id = b.shop_id
GROUP BY a.shop_id, b.shop_name
Question 2: As I wrote, I want to compare two different times. And now, I want to see avg_clicks that is 0 in February 2020.
As a result, the desired output will show me the list of shops that had more than 0 clicks in January, but 0 clicks in February.
Hope I could explain my question. Thanks in advance.
For your Question 1 try to use having clause. Read execution order of SQL statement which gives you a better idea why are you getting avg_clicks() error.
SELECT AVG(a.clicks) AS avg_clicks,
a.shop_id,
b.shop_name
FROM
(SELECT SUM(clicks_on) AS clicks,
shop_id,
date
FROM X
WHERE site = ‘com’
AND date >= '2020-01-01'
AND date <= '2020-01-31'
GROUP BY shop_id, date) as a
JOIN Y as b
ON a.shop_id = b.shop_id
GROUP BY a.shop_id, b.shop_name
HAVING AVG(a.clicks) > 0
For your Question 2, you can do something like this
SELECT
shop_id,
b.shop_name,
jan_avg_clicks,
feb_avg_clicks
FROM
(
SELECT
AVG(clicks) AS jan_avg_clicks,
shop_id
FROM
(
SELECT
SUM(clicks_on) AS clicks,
shop_id,
date
FROM X
WHERE site = ‘com’
AND date >= '2020-01-01'
AND date <= '2020-01-31'
GROUP BY
shop_id,
date
) as a
GROUP BY
shop_id
HAVING AVG(clicks) > 0
) jan
join
(
SELECT
AVG(clicks) AS feb_avg_clicks,
shop_id
FROM
(
SELECT
SUM(clicks_on) AS clicks,
shop_id,
date
FROM X
WHERE site = ‘com’
AND date >= '2020-02-01'
AND date < '2020-03-01'
GROUP BY
shop_id,
date
) as a
GROUP BY
shop_id
HAVING AVG(clicks) = 0
) feb
on jan.shop_id = feb.shop_id
join Y as b
on jan.shop_id = b.shop_id
Start with conditional aggregation:
SELECT shop_id,
SUM(CASE WHEN DATE_TRUNC('month', date) = '2020-01-01' THEN clicks_on END) / COUNT(DISTINCT date) as avg_clicks_jan,
SUM(CASE WHEN DATE_TRUNC('month', date) = '2020-02-01' THEN clicks_on END) / COUNT(DISTINCT date) as avg_clicks_feb
FROM X
WHERE site = 'com' AND
date >= '2020-01-01' AND
date < '2020-03-01'
GROUP BY shop_id;
I'm not sure what comparison you want to make. But if you want to filter based on the aggregated values, use a HAVING clause.

Dropping Data into Aggregate Buckets by Date and Category

I'm trying to display aggregate counts of open and closed IT tickets by date and category.
Parent table consists of the following columns:
Alert_ID Alert_Open_Date Alert_Closed_Date
I'd like my end result to resemble the following, where I have
A. A date within any specified date range,
B. total number of alerts that still showed open as of that date (Outstanding_Alerts),
C. total number of alerts that were opened on that date (New_Alerts),
D. total number of the new alerts that were closed on that date (Closed_New_Alerts), and
E. combined number of alerts, both new and old, that were closed on that date (Closed_Total):
Date Outstanding_Alerts New_Alerts Closed_New_Alerts Closed_Total
6/1/2018 20 10 5 7
6/2/2018 23 20 8 10
6/3/2018 33 13 10 15
etc. # # # #
I was thinking of something like the following conceptual query to accomplish this, but I'm stumbling over the logic to get the results I'd like. Regardless of wording I can't seem to get the buckets correct. Some columns remain blank when they should be populated, for example. Any help is appreciated.
SELECT DISTINCT
alert_date
, SUM(OOA) AS Outstanding_Open_Alerts
, SUM(NOA) AS New_Open_Alerts
, SUM(CNA*NOA) AS Closed_New_Alerts
, SUM(CT) AS Total_Closed_Alerts
, SUM(CNA+NOA-CT) AS Remaining_Alerts --optional column
FROM
(SELECT
TRUNC(open_date) AS Alert_Date
, CASE WHEN alert_date < trunc(SYSDATE)-1 AND closed_date IS NULL THEN 1 ELSE 0
END AS OOA --old open alerts
, CASE WHEN alert_date > trunc(SYSDATE)-1 THEN 1 ELSE 0
END AS NOA --new open alerts
, CASE WHEN closed_date >= trunc(SYSDATE)-1 THEN 1 ELSE 0
END AS CNA --closed new alerts
, CASE WHEN closed_date < trunc(SYSDATE)-1 THEN 1 ELSE 0
END AS CT --closed total
FROM sys_alerts)
GROUP BY alert_date;
If I'm following your description, to get the numbers for an arbitrary date supplied as some_date you want something like:
SELECT
some_date AS Alert_Date
, COUNT(CASE WHEN open_date < some_date
AND (closed_date IS NULL OR closed_date > some_date
THEN alert_id END) AS Current_Open_Alerts
, COUNT(CASE WHEN open_date >= some_date
AND open_date < some_date + 1
THEN alert_id END AS New_Alerts
, COUNT(CASE WHEN open_date >= some_date
AND open_date < some_date + 1
AND closed_date < some_date + 1
THEN alert_id END AS Closed_New_Alerts
, COUNT(CASE WHEN closed_date >= some_date
AND closed_date < some_date + 1
THEN alert_id END AS Closed_Total
, COUNT(CASE WHEN open_date < some_date + 1
AND (closed_date IS NULL OR closed_date >= some_date + 1
THEN alert_id END) AS Alerts_Remaining
FROM sys_alerts
GROUP BY some_date
I've used a count rather than a sum, which relies on the fact that count ignores nulls - and left the default from the case expresssions as null. It doesn't need a sunquery really so I've removed that.
It seems like you want all data for a range of dates, so you can generate that in an inline view or CTE and then join to your real table, so some_date becomes wach individual date from that generated range. E.g. to get the last 30 days something like:
FROM (
SELECT sysdate - level AS some_date
FROM dual
CONNECT BY level <= 30
LEFT JOIN sys_alerts
ON closed_date IS NULL OR closed_date >= some_date
GROUP BY some_date
ORDER BY some_date
or you could do something with cross apply or a partitioned outer join. Hopefully this gives you a starting point though.

SQL, sum in column(s) based on date column

I had a long break from SQL and am a bit rusty. Let's say I have 3 columns in my table: username, value_of_sale, date.
I need a select statement to show me 4 columns based on this table:
username
sum of value_of_sales (where date = today)
sum of value_of_sales (where date = today-1)
sum of value_of_sales (where date = today-2)
I am using db2.
Use SUM and GROUP BY.
SELECT username,
(CASE WHEN date = CURRENT_DATE THEN value_of_sale ELSE 0 END) AS SUMtoday,
(CASE WHEN date = DAYOFYEAR(CURRENT_DATE) - 1 ) THEN value_of_sale ELSE 0 END) AS SUMtoday-1,
(CASE WHEN date = DAYOFYEAR(CURRENT_DATE) - 2 ) THEN value_of_sale ELSE 0 END) AS SUMtoday-2
FROM yourtable
GROUP BY username
Without trying to merge all three of those together, it's very simple to do them individually:
Today:
SELECT username, SUM(value_of_sale) FROM `tableName` WHERE DATE(`date`) = CURDATE() GROUP BY username
Yesterday:
SELECT username, SUM(value_of_sale) FROM `tableName` WHERE DATE(`date`) = DATE_ADD(CURDATE(), INTERVAL -1 DAY) GROUP BY username
Two Days Ago:
SELECT username, SUM(value_of_sale) FROM `tableName` WHERE DATE(`date`) = DATE_ADD(CURDATE(), INTERVAL -2 DAY) GROUP BY username
DATE('date') takes the date portion of the timestamp. If your data is just listed in dates, then you can just compare 'date' to today's date.
CURDATE() gets today's date in the server.
DATE_ADD() is used to add or subtract days.
I have three fields in my table id,sale_amount,created_at(Sales Date)
SELECT (SELECT sum(sale_amount) FROM `sales` WHERE created_at =
CURDATE()) AS todaySale,(SELECT sum(sale_amount) FROM `sales` WHERE
created_at = CURDATE() -1) AS YesterdaySale,(SELECT sum(sale_amount)
FROM `sales` WHERE created_at = CURDATE() - 2) AS Last2DaysSale
This will help you for sure. Thank You
Probably not the most efficient method, but ...
select username,
sum(case when date = today then value_of_sales else 0 end) [Today],
sum(case when date = today-1 then value_of_sales else 0 end) [Yesterday],
sum(case when date = today-2 then value_of_sales else 0 end) [TheDayBefore]
from table
group by username