I have data that contains everyday data for each id. I have unique 96 ids. So for each id there are 365 days recorded like
date ids data
01.01.2022 1 0
02.01.2022 1 0
03.01.2022 1 0
04.01.2022 1 0
05.01.2022 1 0
06.01.2022 1 321
07.01.2022 1 0
08.01.2022 1 0
09.01.2022 1 0
10.01.2022 1 0
11.01.2022 1 0
12.01.2022 1 0
13.01.2022 1 0
14.01.2022 1 0
15.01.2022 1 0
16.01.2022 1 0
17.01.2022 1 0
18.01.2022 1 0
19.01.2022 1 0
20.01.2022 1 0
21.01.2022 1 0
01.01.2022 2 434
02.01.2022 2 0
03.01.2022 2 0
04.01.2022 2 0
05.01.2022 2 0
06.01.2022 2 0
07.01.2022 2 0
…
01.05.2022 1 3213
02.05.2022 1 0
03.05.2022 1 0
04.05.2022 1 0
05.05.2022 1 0
06.05.2022 1 0
07.05.2022 1 0
08.05.2022 1 0
09.05.2022 1 0
So I need to select the first 7 days from the point where the data column is not 0 or null. From this example, we can see that I should get all rows between 06.01.2022 and 12.01.2022 for id - 1, and between 01.01.2022 and 07.01.2022 for id - 2, and so on. In addition, the id can be repeated again on other dates. For example, for id there is new data on 01.05.2022, so we take this data too with the next 7 consecutive days. How select query will look for this question in PostgreSQL? I hope I could deliver the idea
You can get all non-zero and non-null rows in a with CTE and join that with your table, adding anything up to 6 days from that - assuming you're after first 7 days including the one with non-empty data.
with first_non_zeros as (
select ids,date
from test
where data<>0 --and data is not null --the "not null" isn't necessary
)
select t.*
from test t
inner join first_non_zeros f
on t.ids=f.ids and t.date-f.date between 0 and 6
order by ids,date;
date | ids | data
-----------+-----+------
2022-01-06 | 1 | 321
2022-01-07 | 1 | 0
2022-01-08 | 1 | 0
2022-01-09 | 1 | 0
2022-01-10 | 1 | 0
2022-01-11 | 1 | 0
2022-01-12 | 1 | 0
2022-05-01 | 1 | 3213
2022-05-02 | 1 | 0
2022-05-03 | 1 | 0
2022-05-04 | 1 | 0
2022-05-05 | 1 | 0
2022-05-06 | 1 | 0
2022-05-07 | 1 | 0
2022-01-01 | 2 | 434
2022-01-02 | 2 | 0
2022-01-03 | 2 | 0
2022-01-04 | 2 | 0
2022-01-05 | 2 | 0
2022-01-06 | 2 | 0
2022-01-07 | 2 | 0
Online demo
Related
I've something to execute I need the count based on values. Here is my Table
"ORD_NUM","ORD_AMOUNT","ORD_DATE","CUST_CODE","AGENT_CODE","ORD_DESCRIPTION"
"200118"|"500"|"07/20/2008"|"C00023"|"A006"|"SOD"
"200120"|"500"|"07/20/2008"|"C00009"|"A002"|"SOD"
"200129"|"1000"|"07/20/2008"|"C00024"|"A006"|"SOD"
"200127"|"1000"|"08/20/2008"|"C00015"|"A003"|"SOD"
"200128"|"500"|"08/20/2008"|"C00009"|"A002"|"SOD"
"200128"|"500"|"09/20/2008"|"C00009"|"A002"|"SOD"
"200128"|"1000"|"09/20/2008"|"C00009"|"A002"|"SOD"
"200128"|"1000"|"10/20/2008"|"C00009"|"A002"|"SOD"
In the order amount we only have either 1000 or 500. We need to return the data count of 500 and 1000 for each date. EX:
Date |1000Count|500Count
07/20/2008| 1 | 2
08/20/2008| 1 | 1
09/20/2008| 1 | 1
10/20/2008| 1 | 1
Thanks! to SlavaRozhnev
select
d,
count(case when amount = 500 then 1 end) count500,
count(case when amount = 1000 then 1 end) count1000
from test
group by d;
Gives Result :
+============+==========+===========+
| d | count500 | count1000 |
+============+==========+===========+
| 2023-01-01 | 4 | 2 |
| 2023-01-02 | 1 | 1 |
| 2023-01-03 | 0 | 1 |
| 2023-01-04 | 1 | 0 |
+------------+----------+-----------+
Consider the following 2 tables.
TableDE
ID country key1 key2
------------------------
1 US 1 null
1 US 1 null
1 US 1 null
2 US null null
3 US 1 1
4 DE 1 1
5 DE null null
5 DE null null
TableUS
ID key1 key2
--------------
1 null null
2 null 1
4 1 1
8 null 1
2 null 1
2 null 1
9 1 null
I need a distinct overview of all IDs, combining data from both tables:
ID inTableDe country DEkey1 DEkey2 inTableUS USkey1 USKey2
-----------------------------------------------------------------
1 1 US 1 0 1 0 0
2 1 US 0 0 1 0 1
3 1 US 1 1 0 0 0
4 1 DE 1 1 1 1 1
5 1 DE 0 0 0 0 0
8 0 0 0 1 1 0 1
9 0 0 0 1 1 1 0
I hope it speaks for itself:
ID 8 and ID 9 have 0 in the first column bc they aren't in tableDE
ID 8 and ID 9 have 0 in the country column bc this field doesn't exist in tableUS
ID 3 has 0 in inTableUS bc it only exists in tableDE
the key values are copied from the original tables
an ID is not unique: it can appear many times in both tables. However: the values for key1 and key2 will always be the same for each ID within the same table.
I have been messing for hours now with this; I have this now:
select de.[ID],
de.[country],
case when (de.[ID] in (select distinct [ID] from [tableDE]) then 1 else 0 end as [inTableDE],
case when (de.[ID] in (select distinct [ID] from [tableUS]) then 1 else 0 end as [inTableUS],
de.[key1] as [DEKey1],
de.[key2] as [DEKey2],
us.[key1] as [USKey1],
us.[key2] as [USKey2],
from dbo.[tableDE] de
full outer join dbo.[tableUS] us on de.[ID] = us.[ID]
where de.[country] = 'US'
and (de.[key1] = 1 or de.[key2] = 1 or us.[key1] = 1 or us.[key2] = 1)
group by de.[ID], us.[ID]
But this keeps giving me only values that are in both tables.
What am I doing wrong?
You sem to want aggregation on top of the full join:
select
coalesce(de.id, us.id) as id,
case when de.id is null then 0 else 1 end as intablede,
max(de.country) as country,
coalesce(max(de.key1), 0) as dekey1,
coalesce(max(de.key2), 0) as dekey2,
case when us.id is null then 0 else 1 end as intableus,
coalesce(max(us.key1), 0) as uskey1,
coalesce(max(us.key2), 0) as uskey2
from dbo.tablede de
full join dbo.tableus us on de.id = us.id
group by de.id, us.id
order by id
Demo on DB Fiddle:
id | intablede | country | dekey1 | dekey2 | intableus | uskey1 | uskey2
-: | --------: | :------ | -----: | -----: | --------: | -----: | -----:
1 | 1 | US | 1 | 0 | 1 | 0 | 0
2 | 1 | US | 0 | 0 | 1 | 0 | 1
3 | 1 | US | 1 | 1 | 0 | 0 | 0
4 | 1 | DE | 1 | 1 | 1 | 1 | 1
5 | 1 | DE | 0 | 0 | 0 | 0 | 0
8 | 0 | null | 0 | 0 | 1 | 0 | 1
9 | 0 | null | 0 | 0 | 1 | 1 | 0
I wish SQL for SUM each column(IPO and UOR) in TOTAL in second last. And GRAND TOTAL(Sum IPO + UOR) in the last one. Thank you so much
No Code IPO UOR
----------------------
1 D173 1 0
2 D176 3 0
3 D184 1 1
4 D185B 1 0
5 D187 1 2
6 F042 3 0
7 ML004 12 3
8 TTPMC 2 0
9 Z00204 1 0
------------------
TOTAL (NOS) 25 6
-------------------------
GRAND TOTAL (NOS) 31
Here is my code, :
SELECT
SUM(CASE WHEN IPOType = 'IPO' THEN 1 ELSE 0 END) as IPO,
SUM(CASE WHEN IPOType = 'UOR' THEN 1 ELSE 0 END) as UOR
FROM IPO2018
GROUP BY OriProjNo
it can show like this
No Code IPO UOR
----------------------
1 D173 1 0
2 D176 3 0
3 D184 1 1
4 D185B 1 0
5 D187 1 2
6 F042 3 0
7 ML004 12 3
8 TTPMC 2 0
9 Z00204 1 0
------------------
Generally speaking, you want to leave totals and sub-totals to whatever tool you are presenting your data in, as they will be able to handle the formatting with significantly more ease. In addition, your desired output does not have the same number of columns (Grand Total row only has one numeric) so even if you did shoehorn this in to the same dataset, the column headings wouldn't make sense.
That said, you can return group totals via the with rollup statement. This will provide an additional row with the aggregate totals for the group. Where there is more than one group in your data, you will get a sub-total row for each group and a total row for the entire dataset:
declare #t table(c nvarchar(10),t nvarchar(3));
insert into #t values ('D173','IPO'),('D176','IPO'),('D176','IPO'),('D176','IPO'),('D184','IPO'),('D184','UOR'),('D185B','IPO'),('D187','IPO'),('D187','UOR'),('D187','UOR'),('F042','IPO'),('F042','IPO'),('F042','IPO'),('TTPMC','IPO'),('TTPMC','IPO'),('Z00204','IPO'),('ML004','UOR'),('ML004','UOR'),('ML004','UOR'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO'),('ML004','IPO');
select row_number() over (order by grouping(c),c) as n
,case when grouping(c) = 1 then 'TOTAL (NOS)' else c end as c
,sum(case when t = 'IPO' then 1 else 0 end) as IPO
,sum(case when t = 'UOR' then 1 else 0 end) as UOR
from #t
group by c
with rollup
order by grouping(c)
,c;
Output:
+----+-------------+-----+-----+
| n | c | IPO | UOR |
+----+-------------+-----+-----+
| 1 | D173 | 1 | 0 |
| 2 | D176 | 3 | 0 |
| 3 | D184 | 1 | 1 |
| 4 | D185B | 1 | 0 |
| 5 | D187 | 1 | 2 |
| 6 | F042 | 3 | 0 |
| 7 | ML004 | 12 | 3 |
| 8 | TTPMC | 2 | 0 |
| 9 | Z00204 | 1 | 0 |
| 10 | TOTAL (NOS) | 25 | 6 |
+----+-------------+-----+-----+
I am finding myself in the position of having to formulate a (to me) rather complex SQL query and I can't seem to get my head around it.
I have a table called orders and a related table order_state_history that logs the state of those orders over time (see below).
I now need to generate a series of rows - one row per day - containing the amount of orders that were in particular states at the end of that day (see report). Also I want to consider only orders of order.type = 1.
The data resides in a PostgreSQL database. I already found out how to generate a time series using GENERATE_SERIES(DATE '2001-01-01', CURRENT_DATE, '1 DAY'::INTERVAL) days which allows me to generate rows for days on which no state changes were recorded.
My current approach is to join orders, order_state_history and the generated series of days all together and try to filter out all the rows that have DATE(order_state_history.timestamp) > DATE(days) and then somehow get the final state of each order on that day by first_value(order_state_history.new_state) OVER (PARTITION_BY(orders.id) ORDER BY order_state_history.timestamp DESC), but this is where my tiny bit of SQL experience abandons me.
I just can't wrap my head around the problem.
Can this even be solved in a single query or would I be better adviced to compute the data by some kind of intelligent script that performs one query per day?
What would be a reasonable approach to the problem?
orders===
id type
10000 1
10001 1
10002 2
10003 2
10004 1
order_state_history===
order_id index timestamp new_state
10000 1 01.01.2001 12:00 NEW
10000 2 02.01.2001 13:00 ACTIVE
10000 3 03.01.2001 14:00 DONE
10001 1 02.01.2001 13:00 NEW
10002 1 03.01.2001 14:00 NEW
10002 2 05.01.2001 10:00 ACTIVE
10002 3 05.01.2001 14:00 DONE
10003 1 07.01.2001 04:00 NEW
10004 1 05.01.2001 14:00 NEW
10004 2 10.01.2001 17:30 DONE
Expected result===
date new_orders active_orders done_orders
01.01.2001 1 0 0
02.01.2001 1 1 0
03.01.2001 1 0 1
04.01.2001 1 0 1
05.01.2001 2 0 1
06.01.2001 2 0 1
07.01.2001 2 0 1
08.01.2001 2 0 1
09.01.2001 2 0 1
10.01.2001 1 0 2
Step 1. Calculate a cumulative sum of state for each order, using values NEW = 1, ACTIVE = 1, DONE = 2:
select
order_id, timestamp::date as day,
sum(case new_state when 'DONE' then 2 else 1 end) over w as state
from order_state_history h
join orders o on o.id = h.order_id
where o.type = 1
window w as (partition by order_id order by timestamp)
order_id | day | state
----------+------------+-------
10000 | 2001-01-01 | 1
10000 | 2001-01-02 | 2
10000 | 2001-01-03 | 4
10001 | 2001-01-02 | 1
10004 | 2001-01-05 | 1
10004 | 2001-01-10 | 3
(6 rows)
Step 2. Calculate a transition matrix for each order based on states from step 1 (2 means NEW->ACTIVE, 3 means NEW->DONE, 4 means ACTIVE->DONE):
select
order_id, day, state,
case when state = 1 then 1 when state = 2 or state = 3 then -1 else 0 end as new,
case when state = 2 then 1 when state = 4 then -1 else 0 end as active,
case when state > 2 then 1 else 0 end as done
from (
select
order_id, timestamp::date as day,
sum(case new_state when 'DONE' then 2 else 1 end) over w as state
from order_state_history h
join orders o on o.id = h.order_id
where o.type = 1
window w as (partition by order_id order by timestamp)
) s
order_id | day | state | new | active | done
----------+------------+-------+-----+--------+------
10000 | 2001-01-01 | 1 | 1 | 0 | 0
10000 | 2001-01-02 | 2 | -1 | 1 | 0
10000 | 2001-01-03 | 4 | 0 | -1 | 1
10001 | 2001-01-02 | 1 | 1 | 0 | 0
10004 | 2001-01-05 | 1 | 1 | 0 | 0
10004 | 2001-01-10 | 3 | -1 | 0 | 1
(6 rows)
Step 3. Calculate a cumulative sum of each state for a series of days:
select distinct
day::date,
sum(new) over w as new,
sum(active) over w as active,
sum(done) over w as done
from generate_series('2001-01-01'::date, '2001-01-10', '1d'::interval) day
left join (
select
order_id, day, state,
case when state = 1 then 1 when state = 2 or state = 3 then -1 else 0 end as new,
case when state = 2 then 1 when state = 4 then -1 else 0 end as active,
case when state > 2 then 1 else 0 end as done
from (
select
order_id, timestamp::date as day,
sum(case new_state when 'DONE' then 2 else 1 end) over w as state
from order_state_history h
join orders o on o.id = h.order_id
where o.type = 1
window w as (partition by order_id order by timestamp)
) s
) s
using(day)
window w as (order by day)
order by 1
day | new | active | done
------------+-----+--------+------
2001-01-01 | 1 | 0 | 0
2001-01-02 | 1 | 1 | 0
2001-01-03 | 1 | 0 | 1
2001-01-04 | 1 | 0 | 1
2001-01-05 | 2 | 0 | 1
2001-01-06 | 2 | 0 | 1
2001-01-07 | 2 | 0 | 1
2001-01-08 | 2 | 0 | 1
2001-01-09 | 2 | 0 | 1
2001-01-10 | 1 | 0 | 2
(10 rows)
I have a table containing rows which are either 'headers' or 'normal', non-header entries. This is tracked by an INTEGER affinity column IsHeader.
Likewise, I have a column tracking if the row is 'Active'.
With a table 'Entries', and another column 'MCL_Row' used to find relevant rows, I can toggle the value of 'Active' using
UPDATE Entries SET(Active) =
(SELECT (~(Active&1))&(Active|1) WHERE MCL_Row = <target>)
WHERE MCL_Row = <target>;
This works, but if I want to toggle an entire group on or off based on the header, I can't use
UPDATE Entries SET(Active) =
(SELECT (~(Active&1))&(Active|1) WHERE S_Type = <typenum> AND IsHeader=1)
WHERE S_Type = <typenum>;
because here, the SELECT subquery returns the one value I want, but multiple rows are updated. As a result, the first row gets the correct result, and subsequent rows satisfying the WHERE S_Type = <typenum> clause are updated with a NULL value.
How can I use the value returned by this subclause to set the values (identically) of multiple rows used by the UPDATE statement?
Edit: Perhaps the question was a little unclear originally, so adding some example before/after data.
Before:
MCL_Row S_Type Active IsHeader
1 1 1 1
2 1 1 0
3 1 0 0
4 2 1 1
5 2 1 0
6 2 1 0
After setting S_Type=1 active via header:
MCL_Row S_Type Active IsHeader
1 1 1 1
2 1 1 0
3 1 >1< 0
4 2 1 1
5 2 1 0
6 2 1 0
After setting S_Type=1 inactive via header:
MCL_Row S_Type Active IsHeader
1 1 >0< 1
2 1 >0< 0
3 1 0 0
4 2 1 1
5 2 1 0
6 2 1 0
1st query
UPDATE Entries
SET Active = 1-Active
WHERE MCL_Row = <target>
;
2nd query
UPDATE Entries
SET Active = (select 1-h.Active
from Entries as h
where h.S_Type = Entries.S_Type
and h.IsHeader = 1
)
WHERE S_Type = <typenum>
Demo
create table Entries (MCL_Row int,S_Type int,IsHeader int,active int);
insert into Entries (MCL_Row,S_Type,IsHeader,active) values
(1,123,1,1)
,(2,123,0,0)
,(3,123,0,0)
,(4,123,0,1)
;
select * from Entries;
+---------+--------+----------+--------+
| MCL_Row | S_Type | IsHeader | active |
+---------+--------+----------+--------+
| 1 | 123 | 1 | 1 |
+---------+--------+----------+--------+
| 2 | 123 | 0 | 0 |
+---------+--------+----------+--------+
| 3 | 123 | 0 | 0 |
+---------+--------+----------+--------+
| 4 | 123 | 0 | 1 |
+---------+--------+----------+--------+
UPDATE Entries
SET Active = (select 1-h.Active
from Entries as h
where h.IsHeader = 1
and h.S_Type = Entries.S_Type
)
WHERE S_Type = 123
;
select * from Entries;
+---------+--------+----------+--------+
| MCL_Row | S_Type | IsHeader | active |
+---------+--------+----------+--------+
| 1 | 123 | 1 | 0 |
+---------+--------+----------+--------+
| 2 | 123 | 0 | 1 |
+---------+--------+----------+--------+
| 3 | 123 | 0 | 1 |
+---------+--------+----------+--------+
| 4 | 123 | 0 | 1 |
+---------+--------+----------+--------+
UPDATE Entries
SET Active = (select 1-h.Active
from Entries as h
where h.IsHeader = 1
and h.S_Type = Entries.S_Type
)
WHERE S_Type = 123
;
select * from Entries;
+---------+--------+----------+--------+
| MCL_Row | S_Type | IsHeader | active |
+---------+--------+----------+--------+
| 1 | 123 | 1 | 1 |
+---------+--------+----------+--------+
| 2 | 123 | 0 | 0 |
+---------+--------+----------+--------+
| 3 | 123 | 0 | 0 |
+---------+--------+----------+--------+
| 4 | 123 | 0 | 0 |
+---------+--------+----------+--------+