I got a table like this:
Id Date1 Date2 Status
----------------------------------------------
1 01/01/2010 null A
2 04/04/2010 05/14/10 X
3 01/01/2010 null A
4 01/11/2010 01/01/2010 X
5 01/02/2010 null A
And several other records, Date 1 is not null but it is only relevant in the group by if the Status is A, for the records where Date2 is not null, regardless of the status the group by should be by this Date2.
The desired result set is as follows:
Date Number of A Status Number of Date 2 not null statuses
------------------------------------------------------------------------------
01//01/2010 2 1
01/02/2010 1 0
05/14/2010 0 1
Basically the group by must group by date, the problem is that in some cases it is for the Date1 column and in the other case is for the Date2 columns. How can this be accomplished?
You can group by a decode, or case, expression. Only tried in Oracle so not sure if this is portable. With this data:
create table t42 as
select 1 id, to_date('01/01/2010') date1, null date2, 'A' status from dual
union select 2, to_date('04/04/2010'), to_date('05/14/2010'), 'X' from dual
union select 3, to_date('01/01/2010'), null, 'A' from dual
union select 4, to_date('01/11/2010'), to_date('01/01/2010'), 'X' from dual
union select 5, to_date('01/02/2010'), null, 'A' from dual
/
select * from t42;
ID DATE1 DATE2 STATUS
---------------------- ------------------------- ------------------------- ------
1 01/01/2010 A
2 04/04/2010 05/14/2010 X
3 01/01/2010 A
4 01/11/2010 01/01/2010 X
5 01/02/2010 A
You can do:
select case when date2 is null and status = 'A' then date1
else date2 end as "Date",
sum(case when status = 'A' then 1 else 0 end) as "Number of A status",
sum(case when date2 is null then 0 else 1 end) as "Number of Date 2 null"
from t42
group by case when date2 is null and status = 'A' then date1 else date2 end
order by 1;
Which gives:
Date Number of A status Number of Date 2 null
------------------------- ---------------------- ----------------------
01/01/2010 2 1
01/02/2010 1 0
05/14/2010 0 1
It's a typical PIVOT query:
SELECT x.date,
SUM(CASE WHEN 'A' IN (y.status, z.status) THEN 1 ELSE 0 END) AS NumStatusA,
SUM(CASE
WHEN y.date2 IS NOT NULL OR z.date2 IS NOT NULL THEN 1
ELSE 0
END) AS NumDate2NotNull
FROM (SELECT a.date1 AS date
FROM YOUR_TABLE a
UNION
SELECT b.date2 AS date
FROM YOUR_TABLE b) x
LEFT JOIN YOUR_TABLE y ON y.date1 = x.date
LEFT JOIN YOUR_TABLE z ON z.date1 = x.date
GROUP BY x.date
But you need to derive a table containing the dates from both columns, based on your data, to join against first.
Maybe something like this:
SELECT DISTINCT Date1 as Date,
(SELECT COUNT(*) FROM MyTable WHERE DATE1=MyT.Date1 AND Status = 'A') NumberAStatus,
(SELECT COUNT(*) FROM MyTable WHERE DATE1=MyT.Date1 AND Date2 is not null) NotNullDate2
FROM MyTable MyT
Related
To SELECT multiple CASE-WHEN expressions into a single row per ID I have used aggregation MAX and GROUPBY.
SELECT table1.IDvar,
MAX(CASE WHEN table2.var1 = 'foo' THEN table2.var2 END) AS condition1,
MAX(CASE WHEN table2.var1 = 'bar' THEN table2.var2 END) AS condition2
FROM table1
FULL JOIN table2 ON table1.IDvar = table2.table1_IDvar
GROUP BY table1.IDvar
However, I have observed that empirical criteria such as foo entered in the CASE-WHEN-THEN-END expression may occur multiple times, that is, in multiple rows each of which corresponds to different values on columns of interest (THEN column-of-interest END) in the db schema. This implies that taking the MAX or MIN drops data that may be of interest. It is not known in advance how many rows there are for each value in criteria_col and thus in the cols_of_interest.
Sample data e.g.:
IDvar_foreign_key
criteria_col
col_of_interest1
col_of_interest2
x1
foo
01-01-2021
100
x1
foo
01-06-2021
2000
x1
foo
01-08-2021
0
x1
bar
01-08-2021
300
Note: the actual table does contain a unique identifier or primary key.
Q: Are there ways to pivot certain columns/tables in a db schema without possibly dropping some values?
An ouput something like this:
IDvar_foreign_key
foo_1_col_of_interest1
foo_1_col_of_interest2
foo_2_col_of_interest1
foo_2_col_of_interest2
foo_3_col_of_interest1
foo_3_col_of_interest2
x1
01-01-2021
100
01-06-2021
2000
01-08-2021
0
Edit
#lemon and #MTO suggests dynamic queries are necesarry, otherwise I was considering whether not using aggregation would do
Dynamic Pivot in Oracle's SQL
Pivot rows to columns without aggregate
TSQL Pivot without aggregate function
You can use the MIN and MAX aggregation functions and to get the correlated minimums and maximums for col_of_interest2 you can use KEEP (DENSE_RANK ...):
SELECT t1.IDvar,
MIN(CASE WHEN t2.criteria_col = 'foo' THEN t2.col_of_interest1 END)
AS foo_1_col_of_interest1,
MIN(col_of_interest2) KEEP (
DENSE_RANK FIRST ORDER BY
CASE WHEN t2.criteria_col = 'foo' THEN t2.col_of_interest1 END
ASC NULLS LAST
) AS foo_1_col_of_interest2,
MAX(CASE WHEN t2.criteria_col = 'foo' THEN t2.col_of_interest1 END)
AS foo_2_col_of_interest1,
MAX(col_of_interest2) KEEP (
DENSE_RANK FIRST ORDER BY
CASE WHEN t2.criteria_col = 'foo' THEN t2.col_of_interest1 END
DESC NULLS LAST
) AS foo_2_col_of_interest2
FROM table1 t1
FULL JOIN table2 t2
ON t1.IDvar = t2.table1_IDvar
GROUP BY t1.IDvar
Which, for the sample data:
CREATE TABLE table1 ( idvar ) AS
SELECT 1 FROM DUAL UNION ALL
SELECT 2 FROM DUAL UNION ALL
SELECT 3 FROM DUAL;
CREATE TABLE table2 ( table1_idvar, criteria_col, col_of_interest1, col_of_interest2 ) AS
SELECT 1, 'foo', DATE '2021-01-01', 100 FROM DUAL UNION ALL
SELECT 1, 'foo', DATE '2021-03-01', 500 FROM DUAL UNION ALL
SELECT 1, 'foo', DATE '2021-06-01', 2000 FROM DUAL UNION ALL
SELECT 1, 'bar', DATE '2021-06-01', 2000 FROM DUAL UNION ALL
SELECT 2, 'foo', DATE '2021-01-02', 200 FROM DUAL UNION ALL
SELECT 2, 'foo', DATE '2021-03-02', 300 FROM DUAL UNION ALL
SELECT 2, 'bar', DATE '2021-06-02', 400 FROM DUAL UNION ALL
SELECT 3, 'foo', DATE '2021-01-03', 700 FROM DUAL;
Outputs:
IDVAR
FOO_1_COL_OF_INTEREST1
FOO_1_COL_OF_INTEREST2
FOO_2_COL_OF_INTEREST1
FOO_2_COL_OF_INTEREST2
1
2021-01-01 00:00:00
100
2021-06-01 00:00:00
2000
2
2021-01-02 00:00:00
200
2021-03-02 00:00:00
300
3
2021-01-03 00:00:00
700
2021-01-03 00:00:00
700
SQL (not just Oracle) requires each query to have a known, fixed number of columns; if you want a dynamic number of columns then you should perform the pivot in whatever third-party application (Java, C#, PHP, etc.) that you are using to talk to the database.
If you want to pivot a fixed maximum number of columns then you can use the ROW_NUMBER analytic function. For example, if you want the 3 minimum values for col_of_interest1 then you can use:
SELECT idvar,
MAX(CASE WHEN criteria_col = 'foo' AND rn = 1 THEN col_of_interest1 END)
AS foo_1_col_of_interest1,
MAX(CASE WHEN criteria_col = 'foo' AND rn = 1 THEN col_of_interest2 END)
AS foo_1_col_of_interest2,
MAX(CASE WHEN criteria_col = 'foo' AND rn = 2 THEN col_of_interest1 END)
AS foo_2_col_of_interest1,
MAX(CASE WHEN criteria_col = 'foo' AND rn = 2 THEN col_of_interest2 END)
AS foo_2_col_of_interest2,
MAX(CASE WHEN criteria_col = 'foo' AND rn = 3 THEN col_of_interest1 END)
AS foo_3_col_of_interest1,
MAX(CASE WHEN criteria_col = 'foo' AND rn = 3 THEN col_of_interest2 END)
AS foo_3_col_of_interest2
FROM (
SELECT t1.IDvar,
criteria_col,
col_of_interest1,
col_of_interest2,
ROW_NUMBER() OVER (
PARTITION BY t1.IDvar, criteria_col
ORDER BY col_of_interest1, col_of_interest2
) AS rn
FROM table1 t1
FULL JOIN table2 t2
ON t1.IDvar = t2.table1_IDvar
WHERE criteria_col IN ('foo' /*, 'bar', 'etc'*/)
)
GROUP BY idvar
Which outputs:
IDVAR
FOO_1_COL_OF_INTEREST1
FOO_1_COL_OF_INTEREST2
FOO_2_COL_OF_INTEREST1
FOO_2_COL_OF_INTEREST2
FOO_3_COL_OF_INTEREST1
FOO_3_COL_OF_INTEREST2
1
2021-01-01 00:00:00
100
2021-03-01 00:00:00
500
2021-06-01 00:00:00
2000
2
2021-01-02 00:00:00
200
2021-03-02 00:00:00
300
null
null
3
2021-01-03 00:00:00
700
null
null
null
null
fiddle
Unknown number of columns is an issue that you can't ignore. The question is - are there any possible expected limits. I question who would get any meaningfull insight from a resulting dataset with hundreds of columns. It doesnt make sense. If you could setup the limit to 10 or 20 or whatever like that then you could build a datagrid structure using pivot where the number of columns would be the same and the data within could be placed as in your question.
Just as an example how - here is the code that does it with up to 6 pairs of your data of interest (COL_DATE and COL_VALUE) - it could be 20 or 30 or ...
First your sample data and some preparation for pivoting (CTE named grid):
WITH -- S a m p l e d a t a
tbl AS
(
SELECT 1 "ID", 'foo' "CRITERIA", DATE '2021-01-01' "INTEREST_1", 100 "INTEREST_2" FROM DUAL UNION ALL
SELECT 1, 'foo', DATE '2021-03-01', 500 FROM DUAL UNION ALL
SELECT 1, 'foo', DATE '2021-06-01', 2000 FROM DUAL UNION ALL
SELECT 1, 'bar', DATE '2021-06-01', 2000 FROM DUAL UNION ALL
SELECT 2, 'foo', DATE '2021-01-02', 200 FROM DUAL UNION ALL
SELECT 2, 'foo', DATE '2021-03-02', 300 FROM DUAL UNION ALL
SELECT 2, 'bar', DATE '2021-06-02', 400 FROM DUAL UNION ALL
SELECT 3, 'foo', DATE '2021-01-03', 700 FROM DUAL
),
grid AS
(SELECT * FROM
( Select ID "ID", CRITERIA "GRP", INTEREST_1 "COL_DATE", INTEREST_2 "COL_VALUE",
Count(*) OVER(Partition By ID, CRITERIA) "ROWS_TOT",
ROW_NUMBER() OVER(Partition By ID, CRITERIA Order By ID, CRITERIA) "RN_GRP_ID",
ROW_NUMBER() OVER(Partition By ID, CRITERIA Order By ID, CRITERIA) "RN_GRP_ID_2"
From tbl t )
ORDER BY ID ASC, GRP DESC, ROWS_TOT DESC
),
Result (grid)
ID GRP COL_DATE COL_VALUE ROWS_TOT RN_GRP_ID RN_GRP_ID_2
---------- --- --------- ---------- ---------- ---------- -----------
1 foo 01-JAN-21 100 3 3 3
1 foo 01-JUN-21 2000 3 2 2
1 foo 01-MAR-21 500 3 1 1
1 bar 01-JUN-21 2000 1 1 1
2 foo 02-MAR-21 300 2 2 2
2 foo 02-JAN-21 200 2 1 1
2 bar 02-JUN-21 400 1 1 1
3 foo 03-JAN-21 700 1 1 1
... next is pivoting (another CTE named grid_pivot) and designing another grid that will be populated with your data of interest...
grid_pivot AS
( SELECT
ID, GRP, ROWS_TOT,
MAX(GRP_1_LINK) "GRP_1_LINK", CAST(Null as DATE) "GRP_1_DATE", CAST(Null as NUMBER) "GRP_1_VALUE",
MAX(GRP_2_LINK) "GRP_2_LINK", CAST(Null as DATE) "GRP_2_DATE", CAST(Null as NUMBER) "GRP_2_VALUE",
MAX(GRP_3_LINK) "GRP_3_LINK", CAST(Null as DATE) "GRP_3_DATE", CAST(Null as NUMBER) "GRP_3_VALUE",
MAX(GRP_4_LINK) "GRP_4_LINK", CAST(Null as DATE) "GRP_4_DATE", CAST(Null as NUMBER) "GRP_4_VALUE",
MAX(GRP_5_LINK) "GRP_5_LINK", CAST(Null as DATE) "GRP_5_DATE", CAST(Null as NUMBER) "GRP_5_VALUE",
MAX(GRP_6_LINK) "GRP_6_LINK", CAST(Null as DATE) "GRP_6_DATE", CAST(Null as NUMBER) "GRP_6_VALUE"
-- ... ... ... ...
FROM
( Select *
From ( Select * From grid )
PIVOT ( Max(RN_GRP_ID) "LINK" --Min(RN_GRP_ID) "GRP_FROM",
FOR RN_GRP_ID_2 IN(1 "GRP_1", 2 "GRP_2", 3 "GRP_3", 4 "GRP_4", 5 "GRP_5", 6 "GRP_6" ) ) -- ... ...
Order By ROWS_TOT DESC, GRP DESC, ID ASC
)
GROUP BY GRP, ROWS_TOT, ID
ORDER BY ROWS_TOT DESC, GRP DESC, ID ASC
)
Result (grid_pivot)
ID GRP ROWS_TOT GRP_1_LINK GRP_1_DATE GRP_1_VALUE GRP_2_LINK GRP_2_DATE GRP_2_VALUE GRP_3_LINK GRP_3_DATE GRP_3_VALUE GRP_4_LINK GRP_4_DATE GRP_4_VALUE GRP_5_LINK GRP_5_DATE GRP_5_VALUE GRP_6_LINK GRP_6_DATE GRP_6_VALUE
---------- --- ---------- ---------- ---------- ----------- ---------- ---------- ----------- ---------- ---------- ----------- ---------- ---------- ----------- ---------- ---------- ----------- ---------- ---------- -----------
1 foo 3 1 2 3
2 foo 2 1 2
3 foo 1 1
1 bar 1 1
2 bar 1 1
... and, finaly, mixing grid_pivot data with grid data using 6 left joins to fit 6 pairs of your data of interest into the grid.
SELECT gp.ID, gp.GRP,
g1.COL_DATE "GRP_1_DATE", g1.COL_VALUE "GRP_1_VALUE",
g2.COL_DATE "GRP_2_DATE", g2.COL_VALUE "GRP_2_VALUE",
g3.COL_DATE "GRP_3_DATE", g3.COL_VALUE "GRP_3_VALUE",
g4.COL_DATE "GRP_1_DATE", g4.COL_VALUE "GRP_4_VALUE",
g5.COL_DATE "GRP_2_DATE", g5.COL_VALUE "GRP_5_VALUE",
g6.COL_DATE "GRP_3_DATE", g6.COL_VALUE "GRP_6_VALUE"
-- ... ... ... ...
FROM grid_pivot gp
LEFT JOIN grid g1 ON(g1.ID = gp.ID And g1.GRP = gp.GRP And g1.RN_GRP_ID = gp.GRP_1_LINK)
LEFT JOIN grid g2 ON(g2.ID = gp.ID And g2.GRP = gp.GRP And g2.RN_GRP_ID = gp.GRP_2_LINK)
LEFT JOIN grid g3 ON(g3.ID = gp.ID And g3.GRP = gp.GRP And g3.RN_GRP_ID = gp.GRP_3_LINK)
LEFT JOIN grid g4 ON(g4.ID = gp.ID And g4.GRP = gp.GRP And g4.RN_GRP_ID = gp.GRP_4_LINK)
LEFT JOIN grid g5 ON(g5.ID = gp.ID And g5.GRP = gp.GRP And g5.RN_GRP_ID = gp.GRP_5_LINK)
LEFT JOIN grid g6 ON(g6.ID = gp.ID And g6.GRP = gp.GRP And g6.RN_GRP_ID = gp.GRP_6_LINK)
-- ... ... ... ...
ORDER BY gp.ROWS_TOT DESC, gp.GRP DESC, gp.ID ASC
R e s u l t :
ID GRP GRP_1_DATE GRP_1_VALUE GRP_2_DATE GRP_2_VALUE GRP_3_DATE GRP_3_VALUE GRP_1_DATE GRP_4_VALUE GRP_2_DATE GRP_5_VALUE GRP_3_DATE GRP_6_VALUE
---------- --- ---------- ----------- ---------- ----------- ---------- ----------- ---------- ----------- ---------- ----------- ---------- -----------
1 foo 01-MAR-21 500 01-JUN-21 2000 01-JAN-21 100
2 foo 02-JAN-21 200 02-MAR-21 300
3 foo 03-JAN-21 700
1 bar 01-JUN-21 2000
2 bar 02-JUN-21 400
Anyway you will probably need dynamic solution so, this could be interesting for something else, who knows what, when and where...
let's say there's a table have data like below
id
status
date
1
4
2022-05
2
3
2022-06
I want find count of id of each month by their status. Something like this below
date
count(status1) = 4
count(status2) =3
2022-05
1
null
2022-06
null
1
I tried doing
-- select distinct (not working)
select date, status1, status2 from
(select date, count(id) as "status1" from myTable
where status = 4 group by date) as myTable1
join
(select date, count(id) as "status2" from myTable
where status = 3 group by date) as myTable2
on myTable1.date = myTable2.date;
-- group by (not working)
but it does duplicate the data needed.
and I am using SQL Server.
select d.date,
sum
(
case
when d.status=4 then 1
else 0
end
)count_status_4,
sum
(
case
when d.status=5 then 1
else 0
end
)count_status_5
from your_table as d
group by d.date
I have dataset which looks like this:
UserID AccountID CloseDate
1 1000 14/3/2022
1 2000 16/3/2022
2 1000 NULL
2 2000 4/3/2022
2 3000 NULL
And I would like to check if within one user_id all of the close dates are not null. In other words if all accounts within user_id are closed. I was trying using MAX or MIN but it is not working as I expected, because it is simply avoiding NULL values. Is there any other function which can check it? Let's say that my output would be another column which will assign 1 when all CloseDates are not null and else 0.
Sample output:
UserID AccountID CloseDate Check
1 1000 14/3/2022 1
1 2000 16/3/2022 1
2 1000 NULL 0
2 2000 4/3/2022 0
2 3000 NULL 0
Use conditional aggregation to explicitly COUNT the rows where the column has the value NULL:
SELECT GroupedColumn,
COUNT(CASE WHEN NullableColumn IS NULL THEN 1 END) AS NullCount
FROM dbo.YourTable
GROUP BY GroupedColumn;
If you want to just have a 1 or 0 just wrap the count in a CASE expression:
CASE COUNT(CASE WHEN NullableColumn IS NULL THEN 1 END) WHEN 0 THEN 1 ELSE 0 END
You can try to use FIRST_VALUE condition window function
SELECT *,
FIRST_VALUE(IIF(CloseDate IS NULL,0,1)) OVER(PARTITION BY UserID ORDER BY CloseDate )
FROM T
sqlfiddle
with dataset as (select 1 as UserId, 1000 as AccountID, '14/3/2022' as CloseDate
union all select 1, 2000, '16/3/2022'
union all select 2, 1000, NULL
union all select 2, 2000, '4/3/2022'
union all select 2, 3000, NULL)
select userid from dataset
group by userid
having sum(case when closedate is null then 1 else 0 end) = 0;
select d.*, iif(chk>0, 0, 1) chk
from d
outer apply (
select UserId, COUNT(*) CHK
from d dd
WHERE d.UserId = dd.UserId
and dd.CloseDate IS NULL
group by UserId
) C
You can also use "exists". e.g. :
select y.UserID, y.AccountID, y.CloseDate,
-- [Check]: returns 0 if there is a row in the table for the
-- UserID where CloseDate is null, else 1
(case when exists(select * from YourTable y2 where y2.UserID = y.UserID
AND y2.CloseDate is null) then 0 else 1 end) as [Check]
from YourTable y
Im using ORACLE TO check condition in 'DATE_PERIOS' with current time and last time
my ORD0011 table:
-------------------------------------------------------------
ORDER_ID | BS_NO | DATE_PERIOS | STATUS
-------------------------------------------------------------
3000003 HS00001 4-2021 COMPLETE
3000003 HS00183 5-2021 COMPLETE
3000003 HS00776 10-2021 FALSE
3000003 HS00559 11-2021 COMPLETE
3000003 HS00221 12-2021 ACTIVE
3000003 HS00222 1-2022 COMPLETE
--------------------------------------------------------------
when i select 'ORDER_ID' = 3000003, it will output with data following as:
------------------------------
ORDER_ID | HS_TIME
------------------------------
3000003 4
------------------------------
this is my recipe:
If i select DATE_PERIOS is: 1/2022, it will display HS_TIME is 4 (3 times COMPLETE before: 4-2021, 5-2021, 11-2021 + 1)
If i select DATE_PERIOS is: 11/2021, it will display HS_TIME is 3 (2 times COMPLETE before: 4-2021, 5-2021 + 1)
Note: only + 1 when status is COMPLETE
How to count number of Month with condition to output result as above ? Thanks a lot
You can use the below query having case statement to handle the status = 'COMPLETE' -
SELECT CASE WHEN COUNT(CASE WHEN status = 'COMPLETE' THEN 1 ELSE NULL END) > 0 THEN
COUNT(CASE WHEN status = 'COMPLETE' THEN 1 ELSE NULL END) + 1
ELSE NULL
END HS_TIME
FROM tb
WHERE TO_DATE(DATE_PERIOS, 'MM-YYYY') < TO_DATE('01-2020', 'MM-YYYY');
Demo.
One option is to conditionally (that's the CASE expression) add 1 (that's the SUM function) if status is COMPLETE.
WHERE clause requires TO_DATE with appropriate date format. Otherwise, you'd be comparing strings which would lead to wrong result; might be OK if date_perios was stored in YYYYMM format; on the other hand, perhaps you'd want to consider storing date values into the DATE datatype column.
Sample data
SQL> with ord0011 (order_id, date_perios, status) as
2 (select 303, '4-2021' , 'COMPLETE' from dual union all
3 select 303, '5-2021' , 'COMPLETE' from dual union all
4 select 303, '10-2021', 'FALSE' from dual union all
5 select 303, '11-2021', 'COMPLETE' from dual union all
6 select 303, '12-2021', 'ACTIVE' from dual union all
7 select 303, '1-2022' , 'COMPLETE' from dual
8 )
Query itself
9 select order_id,
10 sum(case when status = 'COMPLETE' then 1 else 0 end) hs_time
11 from ord0011
12 where to_date(date_perios, 'mm-yyyy') <= to_date('&par_date', 'mm-yyyy')
13 group by order_id;
Enter value for par_date: 1-2022
ORDER_ID HS_TIME
---------- ----------
303 4
SQL> /
Enter value for par_date: 11-2021
ORDER_ID HS_TIME
---------- ----------
303 3
SQL>
with cte as(
select *,row_number()over(partition by ORDER_ID order by ORDER_ID) as seq
from tb
),
cte2 as(
select *,
case
when DATE_PERIOS = '1-2022'
then (select count(*) + 1 from cte t2 where t1.seq > t2.seq and t2.STATUS = 'COMPLETE')
else 0 end as HS_TIME_1_2022,
case
when DATE_PERIOS = '11-2021'
then (select count(*) + 1 from cte t2 where t1.seq > t2.seq and t2.STATUS = 'COMPLETE')
else 0 end as HS_TIME_11_2022
from cte t1)
select ORDER_ID,max(HS_TIME_1_2022)HS_TIME_1_2022,max(HS_TIME_11_2022)HS_TIME_11_2022
from cte2
group by ORDER_ID
The following output is taken in sql-server but can also be run in Oracle.
Result
I get crazy because of one query. I have a table like following and I want to get a data - Summa of Values by Status For every Date in interval.
Table
Id Name Value Date Status
1 pro1 2 01.04.14 0
2 pro1 8 02.04.14 1
3 pro2 6 02.04.14 1
4 pro3 0 03.04.14 0
5 pro4 7 03.04.14 0
6 pro4 2 03.04.14 0
7 pro4 4 03.04.14 1
8 pro4 6 04.04.14 1
9 pro4 1 04.04.14 1
For example,
Input: Name = pro4, minDate = 01.02.14, maxDate = 04.09.14
Output:
Date Values sum for 0 Status Values sum for 1 Status
01.04.14 0 0
02.04.14 0 0
03.04.14 9 (=7+2) 4 (only 4 exist)
04.04.14 0 7 (6+1)
In 01.02.14 and 02.04.14 dates, pro4 has not values by status, but I want to show that rows, because I need all dates in that interval. Can anyone help me to create this query?
Edit:
I can not change structure, I have already that table with data. Every day exist in table many times (minimum 1 time)
Thanks in advance.
Assuming you have a row for each date in the table, use conditional aggregation:
select date,
sum(Case when name = 'pro4' and status = 0 then Value else 0 end) as values_0,
sum(case when name = 'pro4' and status = 1 then Value else 0 end) as values_1
from Table t
where date >= '2014-04-01' and date <= '2014-04-09'
group by date
order by date;
If you don't have this list of dates, you can take this approach instead:
with dates as (
select cast('2014-04-01' as date) as thedate
union all
select dateadd(day, 1, thedate)
from dates
where thedate < '2014-04-09'
)
select dates.thedate,
sum(Case when status = 0 then Value else 0 end) as values_0,
sum(case when status = 1 then Value else 0 end) as values_1
from dates left outer join
table t
on t.date = dates.thedate and t.name = 'pro4'
group by dates.thedate;
just an assumption query :
select Distinct date ,case when status = 0 and MAX(date) then SUM(value) ELSE 0 END Status0 ,
case when status = 1 and MAX(date) then SUM(value) ELSE 0 END Status1 from table
To expand my comment the complete query is
WITH [counter](N) AS
(SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1)
, days(N) AS (
SELECT row_number() over (ORDER BY (SELECT NULL)) FROM [counter])
, months (N) AS (
SELECT N - 1 FROM days WHERE N < 13)
, calendar ([date]) AS (
SELECT DISTINCT cast(dateadd(DAY, days.n
, dateadd(MONTH, months.n, '20131231')) AS date)
FROM months
CROSS JOIN days
)
SELECT a.Name
, c.Date
, [Sum of 0] = SUM(CASE Status WHEN 0 THEN Value ELSE 0 END)
, [Sum of 1] = SUM(CASE Status WHEN 1 THEN Value ELSE 0 END)
FROM Calendar c
LEFT JOIN myTable a ON c.Date = a.Date AND a.name = 'pro4'
WHERE c.date BETWEEN '20140201' AND '20140904'
GROUP BY c.Date, a.Name
ORDER BY c.Date
Note that the condition on the name need to be in the JOIN, otherwise you'll get only the date of your table.
If you need multiple years just add another CTE for the count and a dateadd(YEAR,...) in the CTE calendar
This is not really the exact query, but I think you can get that by having a query that looks like:
select date, status, sum(value) from table
where (date between mindate and maxdate) and name = product_name
group by date, status;
this page gives more info.
EDIT
So the above query only gives a part of the answer required by the OP. A LEFT OUTER JOIN of the original table and the result of the above query on thedate and status fields will give the missing info.
e.g.
select x.date, x.status, x.sum_of_values from table as y
left outer join
(select date, status, sum(value) as sum_of_values
from table
where (date between mindate and maxdate) and name = product_name
group by date, status) as x
on y.date= x.date and y.status = x.status
order by x.date;