SQL Trailing Date Count - sql

I have a need to do a calculation based on trailing dates when a customer placed an order.
SQL Code to get the following table:
select Date,Cust, ProdID, OrderLog
from AUDIT AS A
where Date >= '2021-02-07'
and Cust = '477'
and Prod ID = 'X'
order by A.Date desc
Date
Cust
ProdID
OrderLog
2/18/2021
477
X
Null
2/17/2021
477
X
1
2/16/2021
477
X
1
2/15/2021
477
X
1
2/14/2021
477
X
Null
2/13/2021
477
X
Null
What I want to do is count the days from 1st OrderLog Date, 2/15/2021, to last OrderLog Date 2/17/2021. For an outcome of 3.
I tried to do this in a window function using lag/over/partition with no luck. I also tried searching for the solution with no luck.
There is a need to do this in one query containing multiple Cust,PRODID and Dates. The count need to be at Cust and ProdID level.
Thanks for any help!

I think you want:
select Cust, ProdID,
max(date) - max(case when OrderLog = 1 then date end) as diff
from AUDIT AS A
where Date >= '2021-02-07' and
Cust = '477' and Prod ID = 'X'
group by cust, prodid;
Note that date/time functions vary among databases, so your database might use something other than - to subtract two dates.

Related

Postgresql Sum statistics by month for a single year

I have the following table in the database:
item_id, integer
item_name, character varying
price, double precision
user_id, integer
category_id, integer
date, date
1
Pizza
2.99
1
2
'2020-01-01'
2
Cinema
5
1
3
'2020-01-01'
3
Cheeseburger
4.99
1
2
'2020-01-01'
4
Rental
100
1
1
'2020-01-01'
Now I want to get the statistics for the total price for each month in a year. It should include all items as well as a single category both for all the time and specified time period. For example, using this
SELECT EXTRACT(MONTH from date),COALESCE(SUM(price), 0)
FROM item_table
WHERE user_id = 1 AND category_id = 3 AND date BETWEEN '2020-01-01'AND '2021-01-01'
GROUP By date_part
ORDER BY date_part;
I expect to obtain this:
date_part
total
1
5
2
0
3
0
...
...
12
0
However, I get this:
date_part
total
1
5
1) How can I get zero value for a case when no items for a specified category are found? (now it just skips the month)
2) The above example gives the statistics for the selected category within some time period. For all my purposes I need to write 3 more queries (select for all time and all categories/ all the time single category/ single year all categories). Is there a unique query for all these cases? (when some parameters like category_id or date are null )
You can get the "empty" months by doing a right join against a table that contains the month numbers and moving the WHERE criteria into the JOIN criteria:
-- Create a temporary "table" for month numbers
WITH months AS (SELECT * FROM generate_series(1, 12) AS t(n))
SELECT months.n as date_part, COALESCE(SUM(price), 0) AS total
FROM item_table
RIGHT JOIN months ON EXTRACT(MONTH from date) = months.n
AND user_id = 1 AND category_id = 3 AND "date" BETWEEN '2020-01-01'AND '2021-01-01'
GROUP BY months.n
ORDER By months.n;
I'm not quite sure what you want from your second part, but you could take a look at Grouping Sets, Cube and Rollup.

Minimum date per year with flag

Hi I have following data in customer table.
CUST_ID DATE
ab13563590 6/1/2008
ab13563591 1/1/2008
ab13563592 2/1/2008
ab13563593 8/1/2010
ab13563594 7/1/2010
ab13563595 9/1/2008
ab13563596 4/1/2008
ab13563597 10/1/2008
ab13563598 3/1/2009
ab13563599 5/1/2009
I need a calculated flag for which minimum date for every year will be 'Y' and 'N' for rest of them. Expected data should look like
CUST_ID DATE FLAG
ab13563590 6/1/2008 N
ab13563591 1/1/2008 Y
ab13563592 2/1/2008 N
ab13563593 8/1/2010 N
ab13563594 7/1/2010 Y
ab13563595 9/1/2008 N
ab13563596 4/1/2008 N
ab13563597 10/1/2008 N
ab13563598 3/1/2009 Y
ab13563599 5/1/2009 N
What I did is, I took group by year with min(date) and loaded data into temp table and join back to actual table and wrote a case statement. But I guess it can be solved in a single select statement.
Thanks in advance
You can solve this using Window Functions:
SELECT cust_id,
date,
CASE WHEN date = min(date) OVER (PARTITION BY EXTRACT(YEAR FROM date) ORDER BY date) THEN 'Y' ELSE 'N' END AS Flag
FROM table;
That will compare the date of the current row to the minimum date for the year. If they are equal, then you get a 'Y'.
Try this:
select cust_id,date,
case
when date = (select min(date) as min_date from customer where to_char(cust.date,'yyyy') = to_char(date,'yyyy'))
then 'Y' else 'N' end as flag
from customer cust

Oracle get the last date for one month

I am not very good with Queries and Database.
I have the the following data table
Date ID Value
20160601 1 300
20160607 1 301
20160601 2 600
20160607 2 601
20160501 1 250
20160507 1 240
20160501 2 800
20160507 2 801
my requirement is to select the last date of a given month for each ID and show the value.
for example, If I choose month 5 the result would be:
Date ID Value
20160507 1 240
20160507 2 801
and so on based on the month the user will enter.
I know it may look simple but I am really stuck and I would appreciate some help. Thanks.
Assuming date is an actual date column (as it should be), you can use extract to compare the month value, and then the row_number() over ... analytic function to get the latest row per id value:
select date, id, value
from (select date, id, value,
row_number() over (partition by id order by date desc) as rn
from tbl
where extract(month from date) = 5)
where rn = 1
Of course, I assume that your actual date column is called something else, as date is a reserved word.
Find the maximum date then select all rows with that date.
select *
from table
where date = (select max(date) from table where date like '201605%')

SQL - Counting months passed by a Person Indicator

I'm trying to count the number of months that have passed based on ID, it's possible that for some records the months will not increase by 1 each time (i.e. someone could have a record for 1/1/13 and 3/1/13 but not 2/1/13) however I only want a count of the records in my table. So missing months don't matter.
An example table would be: (notice the missing month and it's irrelevancy).
DATE ID Months Passed
----------- --- --------------
2013-11-01 105 1
2013-12-01 105 2
2014-02-01 105 3
2014-03-01 105 4
Essentially an Excel COUNTIFSin SQL, which I've written:
=COUNTIFS(IDColumn, ID, MonthColumn, "<=" & Month)
Does anyone know of a way to generate the desired column using SQL?
Try ROW_NUMBER(). If you just want the "Months Passed" column to increase by 1 each time, and for each ID, that will do the trick.
SELECT
Date,
Id,
Indicator,
ROW_NUMBER() OVER(PARTITION BY Id ORDER BY Date) AS RowNum
FROM YourTable
WHERE Indicator = 'YES'
UNION
SELECT
Date,
Id,
Indicator,
0 AS RowNum
FROM YourTable
WHERE Indicator = 'NO'
You could more simply count rows grouped by month (more complex if you have count months in different years separately):
SELECT COUNT(derived.monthVal)
FROM (SELECT MONTH(<your date field>) AS monthVal
FROM [your table]
WHERE [Your ID Column] = <the id>
GROUP BY MONTH(<your date field>)) AS derived;

query to display additional column based on aggregate value

I've been mulling on this problem for a couple of hours now with no luck, so I though people on SO might be able to help :)
I have a table with data regarding processing volumes at stores. The first three columns shown below can be queried from that table. What I'm trying to do is to add a 4th column that's basically a flag regarding if a store has processed >=$150, and if so, will display the corresponding date. The way this works is the first instance where the store has surpassed $150 is the date that gets displayed. Subsequent processing volumes don't count after the the first instance the activated date is hit. For example, for store 4, there's just one instance of the activated date.
store_id sales_volume date activated_date
----------------------------------------------------
2 5 03/14/2012
2 125 05/21/2012
2 30 11/01/2012 11/01/2012
3 100 02/06/2012
3 140 12/22/2012 12/22/2012
4 300 10/15/2012 10/15/2012
4 450 11/25/2012
5 100 12/03/2012
Any insights as to how to build out this fourth column? Thanks in advance!
The solution start by calculating the cumulative sales. Then, you want the activation date only when the cumulative sales first pass through the $150 level. This happens when adding the current sales amount pushes the cumulative amount over the threshold. The following case expression handles this.
select t.store_id, t.sales_volume, t.date,
(case when 150 > cumesales - t.sales_volume and 150 <= cumesales
then date
end) as ActivationDate
from (select t.*,
sum(sales_volume) over (partition by store_id order by date) as cumesales
from t
) t
If you have an older version of Postgres that does not support cumulative sum, you can get the cumulative sales with a subquery like:
(select sum(sales_volume) from t t2 where t2.store_id = t.store_id and t2.date <= t.date) as cumesales
Variant 1
You can LEFT JOIN to a table that calculates the first date surpassing the 150 $ limit per store:
SELECT t.*, b.activated_date
FROM tbl t
LEFT JOIN (
SELECT store_id, min(thedate) AS activated_date
FROM (
SELECT store_id, thedate
,sum(sales_volume) OVER (PARTITION BY store_id
ORDER BY thedate) AS running_sum
FROM tbl
) a
WHERE running_sum >= 150
GROUP BY 1
) b ON t.store_id = b.store_id AND t.thedate = b.activated_date
ORDER BY t.store_id, t.thedate;
The calculation of the the first day has to be done in two steps, since the window function accumulating the running sum has to be applied in a separate SELECT.
Variant 2
Another window function instead of the LEFT JOIN. May of may not be faster. Test with EXPLAIN ANALYZE.
SELECT *
,CASE WHEN running_sum >= 150 AND thedate = first_value(thedate)
OVER (PARTITION BY store_id, running_sum >= 150 ORDER BY thedate)
THEN thedate END AS activated_date
FROM (
SELECT *
,sum(sales_volume)
OVER (PARTITION BY store_id ORDER BY thedate) AS running_sum
FROM tbl
) b
ORDER BY store_id, thedate;
->sqlfiddle demonstrating both.