MIN value from data set and sum - sql

I have data:
ID DUE AMT
4 2018-03-10 335.75
3 2018-04-10 334.75
1 2018-05-10 333.75
2 2018-06-10 332.75
I need to extract:
least due (03-10)
amt for least due (335.75)
sum of amt column.
Could it be done in single query?

Try keep dense rank:
with tt as (
select 4 id, date '2018-03-10' due, 335.75 amt from dual union all
select 3 id, date '2018-04-10' due, 334.75 amt from dual union all
select 1 id, date '2018-05-10' due, 333.75 amt from dual union all
select 2 id, date '2018-06-10' due, 332.75 amt from dual
)
select min(due) least_due,
min(amt) keep (dense_rank first order by due) amt_for_least_due,
sum(amt) sum_amt
from tt

We can try using analytic functions here:
WITH cte AS (
SELECT ID, DUE, AMOUNT,
SUM(AMOUNT) OVER () AS TOTALAMOUNT,
ROW_NUMBER() OVER (ORDER BY DUE) rn
FROM yourTable
)
SELECT ID, DUE, AMOUNT, TOTALAMOUNT
FROM cte
WHERE rn = 1;

Related

How can I write BigQuery SQL to group data by start and end date of column changing?

ID|FLAG|TMST
1|1|2022-01-01
1|1|2022-01-02
...(all dates between 01-02 and 02-05 have 1, there are rows for all these dates)
1|1|2022-02-15
1|0|2022-02-16
1|0|2022-02-17
...(all dates between 02-17 and 05-15 have 0, there are rows for all these dates)
1|0|2022-05-15
1|1|2022-05-16
->
ID|FLAG|STRT_MONTH|END_MONTH
1|1|202201|202202
1|0|202203|202204
1|1|202205|999912
I have the first dataset and I am trying to get the second dataset. How can I write bigquery SQL to group by the ID then get the start and end month of when a flag changes? If a specific month has both 0,1 flag like month 202202, I would like to consider that month to be a 1.
You might consider below gaps and islands approach.
WITH sample_table AS (
SELECT 1 id, 1 flag, DATE '2022-01-01' tmst UNION ALL
SELECT 1 id, 1 flag, '2022-01-02' tmst UNION ALL
-- (all dates between 01-02 and 02-05 have 1, there are rows for all these dates)
SELECT 1 id, 1 flag, '2022-02-15' tmst UNION ALL
SELECT 1 id, 0 flag, '2022-02-16' tmst UNION ALL
SELECT 1 id, 0 flag, '2022-02-17' tmst UNION ALL
SELECT 1 id, 0 flag, '2022-03-01' tmst UNION ALL
SELECT 1 id, 0 flag, '2022-04-01' tmst UNION ALL
-- (all dates between 02-17 and 05-15 have 0, there are rows for all these dates)
SELECT 1 id, 0 flag, '2022-05-15' tmst UNION ALL
SELECT 1 id, 1 flag, '2022-05-16' tmst
),
aggregation AS (
SELECT id, DATE_TRUNC(tmst, MONTH) month, IF(SUM(flag) > 0, 1, 0) flag
FROM sample_table
GROUP BY 1, 2
)
SELECT id, ANY_VALUE(flag) flag,
MIN(month) start_month,
IF(MAX(month) = ANY_VALUE(max_month), '9999-12-01', MAX(month)) end_month
FROM (
SELECT * EXCEPT(gap), COUNTIF(gap) OVER w1 AS part FROM (
SELECT *, flag <> LAG(flag) OVER w0 AS gap, MAX(month) OVER w0 AS max_month
FROM aggregation
WINDOW w0 AS (PARTITION BY id ORDER BY month)
) WINDOW w1 AS (PARTITION BY id ORDER BY month)
)
GROUP BY 1, part;
Query results

Correlated subquery to handle multiple rows

Consider a query:
WITH RECURSIVE dates0(date) AS
(
VALUES('2022-03-01')
UNION ALL SELECT date(date, '+1 day')
FROM dates0
WHERE date < DATE('2022-03-03')),
q0 AS
(SELECT d.date,
(
--pretend this is a result of an expensive subquery for which we need d.date
SELECT 'Need to insert result of another query here'
----pretend this is a result of an expensive subquery for which we need d.date
) AS vegetables
FROM dates0 d)
SELECT *
FROM q0;
Instead of 'SELECT' between 2 commented lines I need to incorporate result of the following subquery:
SELECT name, SUM(amount) AS amount FROM (
SELECT 'POTATO' AS name,
'2022-03-03' AS date,
3 AS amount
UNION
SELECT 'TURNIP' AS name,
'2022-03-03' AS date,
15 AS amount
UNION
SELECT 'TURNIP' AS name,
'2022-03-03' AS date,
25 AS amount) GROUP BY name
For the end result:
date potato turnip
3/1/2022 0 0
3/2/2022 0 0
3/3/2022 3 40
PlaceHolderPlaceHolderPlaceHolderPlaceHolderPlaceHolder (sorry, SO otherwise considers the post to have insufficient amount of text)
Assuming that your query is a CTE named vegetables, you can do it with a LEFT join of dates0 to vegetables and conditional aggregation:
WITH
RECURSIVE dates0(date) AS (
VALUES('2022-03-01')
UNION ALL
SELECT date(date, '+1 day')
FROM dates0
WHERE date < DATE('2022-03-03')
),
vegetables AS (
SELECT 'POTATO' AS name, '2022-03-03' AS date, 3 AS amount
UNION ALL
SELECT 'TURNIP' AS name, '2022-03-03' AS date, 15 AS amount
UNION ALL
SELECT 'TURNIP' AS name, '2022-03-03' AS date, 25 AS amount
),
q0 AS (
SELECT d.date,
TOTAL(CASE WHEN v.name = 'POTATO' THEN v.amount END) AS potato,
TOTAL(CASE WHEN v.name = 'TURNIP' THEN v.amount END) AS turnip
FROM dates0 d LEFT JOIN vegetables v
ON v.date = d.date
GROUP BY d.date
)
SELECT *
FROM q0;
See the demo.

Get the record with the most recent date. SQL

ID
Date
Value
715
2022-03-20
183.74
715
2022-03-13
86.45
715
2022-03-06
-10.84
715
2022-02-27
291.87
715
2022-02-20
194.58
715
2022-02-13
97.29
715
2022-02-06
0.00
Is there a way to get the ID,and value for the last day of each month provided.
Output
ID
Date
Value
715:
:2022-03-20:
183.74:
715:
:2022-02-27:
291.87:
Thanks, I have already tried
SELECT T.*
FROM (
SELECT ID, value, date,
ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Date ASC) AS rn
FROM table
) t
But doesn't quite do it.
Thanks
Adjust your current query to sort row_number descending and add a partition on the month of your date. Then, add a where clause to pull rn = 1:
SELECT T.*
FROM (
SELECT ID, value, date,
ROW_NUMBER() OVER (PARTITION BY ID, month(Date) ORDER BY Date desc) AS rn
FROM table
) t
where t.rn = 1
If you need to expand this to multiple years, you can inlcude a partition on the year as well:
SELECT T.*
FROM (
SELECT ID, value, date,
ROW_NUMBER() OVER (PARTITION BY ID, month(Date), year(Date) ORDER BY Date desc) AS rn
FROM table
) t
where t.rn = 1
SELECT T.*
FROM (
SELECT ID, value, date,
ROW_NUMBER() OVER (PARTITION BY ID, month(Date) ORDER BY Date desc) AS rn
FROM table
) t
where t.rn = 1

Oracle Running Subtraction

I have the below data. I want to subtract the first row from Total Qty (80) and then subtract the rest of the rows from QTY from the previous row of QTY1.
QTY QTY1 DATE TOTAL QTY
2 78 01-JAN-20 80
1 77 15-JAN-20
46 31 22-JAN-20
16 15 27-JAN-20
Is there a way to do this? Any help is greatly appreciated. Thanks
select
t.*
,first_value(TOTAL_QTY)over(order by DT) - sum(QTY)over(order by DT) as QTY1
from t;
Full example with your sample data:
with T(QTY, DT, TOTAL_QTY) as (
select 2 , to_date('01-JAN-20','dd-mon-yy'),80 from dual union all
select 1 , to_date('15-JAN-20','dd-mon-yy'),null from dual union all
select 46, to_date('22-JAN-20','dd-mon-yy'),null from dual union all
select 16, to_date('27-JAN-20','dd-mon-yy'),null from dual
)
select
t.*
,first_value(TOTAL_QTY)over(order by DT) - sum(QTY)over(order by DT) as QTY1
from t;
Result:
QTY DT TOTAL_QTY QTY1
2 2020-01-01 80 78
1 2020-01-15 77
46 2020-01-22 31
16 2020-01-27 15
SQL tables represent unordered sets. Your question seems to rely on the ordering of the rows. Let me assume you have a column that represents the ordering.
Use a cumulative sum:
select t.*,
sum(total_qty) over () - sum(qty) over (order by <ordering col>) as qty1
from t;
Here is a db<>fiddle.
Something like this (the CTE is just your data): if you add any more stuff later (in the total_qty column), then that would also get added to the total_qty calcuation (as would be typical for additions to, and subtractions from, inventory.
with d as
(select 2 qty, 78 qty1 , to_date('01-JAN-20','dd-mon-rr') datecol, 80 total_qty from dual union all
select 1, 77, to_date('15-JAN-20','dd-mon-rr'),null from dual union all
select 46 , 31, to_date('22-JAN-20','dd-mon-rr'),null from dual union all
select 16 , 15 , to_date('27-JAN-20','dd-mon-rr'),null from dual
)
select sum(total_qty) over (order by datecol) - sum(qty) over (order by datecol)
from d
You can do:
select
qty,
first_value(total_qty) over(order by date)
- sum(qty) over(order by date) as qty1,
date, total_qty
from t
order by date

Incremental count

I have a table with a list of Customer Numbers and Order Dates and want to add a count against each Customer number, restarting from 1 each time the customer number changes, I've sorted the Table into Customer then Date order, and need to add an order count column.
CASE WHEN 'Customer Number' on This row = 'Customer Number' on Previous Row then ( Count = Count on Previous Row + 1 )
Else Count = 1
What is the best way to approach this?
Customer and Dates in Customer then Date order:
Customer Date Count
0001 01/05/18 1
0001 02/05/18 2
0001 03/05/18 3
0002 03/05/18 1 <- back to one here as Customer changed
0002 04/05/18 2
0003 05/05/18 1 <- back to one again
I've just tried COUNT(*) OVER (PARTITION BY Customer ) as COUNT but it doesn't seem to be starting from 1 for some reason when the Customer changes
It's hard to tell what you want, but "to add a count against each Customer number, restarting from 1 each time the customer number changes" sounds as if you simply want:
count(*) over (partition by customer_number)
or maybe that should be the count "up-to" the date of the row:
count(*) over (partition by customer_number order by order_date)
It sound like you just want an analytic row_number() call:
select customer_number,
order_date,
row_number() over (partition by customer_number order by order_date) as num
from your_table
order by customer_number,
order_date
Using an analytic count also works, as #horse_with_no_name suggested:
count(*) over (partition by customer_number order by order_date) as num
Quick demo showing both, with your sample data in a CTE:
with your_table (customer_number, order_date) as (
select '0001', date '2018-05-01' from dual
union all select '0001', date '2018-05-03' from dual
union all select '0001', date '2018-05-02' from dual
union all select '0002', date '2018-05-03' from dual
union all select '0002', date '2018-05-04' from dual
union all select '0003', date '2018-05-05' from dual
)
select customer_number,
order_date,
row_number() over (partition by customer_number order by order_date) as num1,
count(*) over (partition by customer_number order by order_date) as num2
from your_table
order by customer_number,
order_date
/
CUST ORDER_DATE NUM1 NUM2
---- ---------- ---------- ----------
0001 2018-05-01 1 1
0001 2018-05-02 2 2
0001 2018-05-03 3 3
0002 2018-05-03 1 1
0002 2018-05-04 2 2
0003 2018-05-05 1 1