Merge multiple select statements and group them - sql

I am having trouble to find an efficient solution to my problem. I think this is a fairly common issue, that is why I am asking it out here.
Here is the problem.
Let says I have multiple selects such as the following ones :
SELECT CREATE
date
num_created
01-01-2021
10
01-02-2021
2
01-04-2021
13
SELECT Update
date
num_update
01-01-2021
14
01-02-2021
2
01-03-2021
9
SELECT Delete
date
num_delete
01-02-2021
2
01-05-2021
40
I want to have this final output
Final output
date
num_created
num_update
num_deleted
01-01-2021
10
14
0
01-02-2021
2
2
2
01-03-2021
0
9
0
01-04-2021
13
0
0
01-05-2021
0
0
40
*I can't assume that any table has all the dates or have matching dates

It looks like you want count grouped by days:
select
tbls.date,
sum(tbls.num_created) as num_created,
sum(tbls.num_updated) as num_updated,
sum(tbls.num_deleted) as num_deleted
from (
select date, num_created, 0 as num_updated, 0 as num_deleted from tbl_create
union all
select date, 0 as num_created, num_updated, 0 as num_deleted from tbl_update
union all
select date, 0 as num_created, 0 as num_updated, num_deleted from tbl_delete) as tbls
group by tbls.date
Please don't abuse "with statement" when it is not needed (as seen in the other answers).
Edit - extra simple query:
if object_id('tempdb..#tmp_dml_statement') is not null drop table #tmp_dml_statement
select date, num_created, 0 as num_updated, 0 as num_deleted
into #tmp_dml_statement
from tbl_create
union all
select date, 0 as num_created, num_updated, 0 as num_deleted
from tbl_update
union all
select date, 0 as num_created, 0 as num_updated, num_deleted
from tbl_delete

You can perform full outer join between the tables and case statement to choose the date as below (Assume 3 tables/queries as a, b, c):
With temp as
(
select case when a.date is not null then a.date else b.date end as date,num_created,num_update from a full join b on (a.date=b.date)
),
temp1 as
(
select case when temp.date is not null then temp.date else c.date end as date,num_created,num_update,num_delete from temp full join c on (temp.date=c.date)
)
select * from temp1;

assume the tables are:
tbl_create(dt, num_created)
tbl_update(dt, num_updated)
tbl_delete(dt, num_deleted)
start with a (temporary)table that contains all dates from the 3 source tables: all_dates
then (left outer) join the counters from the 3 source tables.
note: 'union' removes duplicates, no need to worry about that (in contrast to 'union all')
note: coalesce(a,b) returns the first non-null parameter, so you have zeroes rather than 'null's
with all_dates(dt) as (
select dt from tbl_create
union
select dt from tbl_update
union
select dt from tbl_delete
)
select all_dates.dt
, coalesce(tbl_create.num_created,0) --
, coalesce(tbl_update.num_updated,0)
, coalesce(tbl_delete.num_deleted,0)
from all_dates
left outer join tbl_create on all_dates.dt = tbl_create.dt
left outer join tbl_update on all_dates.dt = tbl_update.dt
left outer join tbl_delete on all_dates.dt = tbl_delete.dt

Related

How to merge two query results joining same date

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

multiple top n aggregates query defined as a view (or function)?

I couldn't find a past question exactly like this problem. I have an orders table, containing a customer id, order date, and several numeric columns (how many of a particular item were ordered on that date). Removing some of the numberics, it looks like this:
customer_id date a b c d
0001 07/01/22 0 3 3 5
0001 07/12/22 12 0 50 0
0002 06/30/22 5 65 0 30
0002 07/20/22 1 0 19 2
0003 08/01/22 0 0 99 0
I need to sum each numeric column by customer_id, then return the top n customers for each column. Obviously that means a single customer may appear multiple times, once for each column. Assuming top 2, the desired output would look something like this:
column_ranked customer_id sum rank
'a' 001 12 1
'a' 002 6 2
'b' 002 65 1
'b 001 3 2
'c' 003 99 1
'c' 001 53 2
'd' 002 30 1
'd' 001 5 2
(this assumes no date range filter)
My first thought was a CTE to collapse the table into its per-customer sums, then a union from the CTE, with a limit n clause, once for each summed column. That works if the date range is hard-coded into the CTE .... but I want to define this as a view, so it can be called by users something like this:
SELECT * from top_customers_view WHERE date_range BETWEEN ( date1 and date2 )
How can I pass the date restriction down to the CTE? Or am I taking the wrong approach entirely? If a view isn't possible, can it be done as a function? (without using a costly cursor, that is.)
Since the date ranges clearly produce a massive number of combinations you cannot generate a view with them. You can write a query, however, as shown below:
with
p as (select cast ('2022-01-01' as date) as ds, cast ('2022-12-31' as date) as de),
a as (
select top 10 customer_id, 'a' as col, sum(a) as s
from t cross join p where date between ds and de
group by customer_id order by s desc
),
b as (
select top 10 customer_id, 'b' as col, sum(b) as s
from t cross join p where date between ds and de
group by customer_id order by s desc
),
c as (
select top 10 customer_id, 'c' as col, sum(b) as s
from t cross join p where date between ds and de
group by customer_id order by s desc
),
d as (
select top 10 customer_id, 'd' as col, sum(b) as s
from t cross join p where date between ds and de
group by customer_id order by s desc
)
select * from a
union all select * from b
union all select * from c
union all select * from d
order by customer_id, col, s desc
The date range is in the second line.
See db<>fiddle.
Alternatively, you could create a data warehousing solution, but it would require much more effort to make it work.

SQL: How to join two columns in a specific way?

I am working with an Oracle Database and I am new to SQL in general.
I have a table with data and month columns. After filtering the data I have just a few rows left. But I want to get two columns: 1-st column with 12 months listed (1,2,3,4,5,6,7,8,9,10,11,12) and second column with values from original data (if exist) or zeroes.
F.e.: Original data:
MONTH VALUE
9 96
What I want:
MONTH VALUE
1 0
2 0
3 0
4 0
5 0
6 0
7 0
8 0
9 96
10 0
11 0
12 0
I have already tried to use join and union all functions but it didn't work out.
First generate a sequence of 12 months number then use left join
select monthNo, coalesce(Value,0) as value from
(
SELECT 1 MonthNo
FROM dual
CONNECT BY LEVEL <= 12
)A left join originaltable b on A.monthNo=b.month
is this what are you looking for?
WITH tab AS(SELECT LEVEL AS m , null as value FROM DUAL CONNECT BY LEVEL <= 12)
, tab2 AS(SELECT 9 as m, 96 as VALUE FROM DUAL)
select t1.m
,coalesce(t2.value,0) as value
from tab t1
left join tab2 t2 on t1.m = t2.m
order by 1
Bro enjoy...
select months.month ,original_data.VALUE
from original_data
Right JOIN (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)) months(month) on
months.month = original_data.MONTH
order by months.month --optional

SQL query - sum of values by status for date interval

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;

little help with some tsql

Given following table:
rowId AccountId Organization1 Organization2
-----------------------------------------------
1 1 20 10
2 1 10 20
3 1 40 30
4 2 15 10
5 2 20 15
6 2 10 20
How do I identify the records where Organization2 doesn't exist in Organization1 for a particular account
for instance, in the given data above my results will be a single record which will be AccountId 1 because row3 organization2 value 30 doesn't exist in organization1 for that particular account.
SELECT rowId, AccountId, Organization1, Organization2
FROM yourTable yt
WHERE NOT EXISTS (SELECT 1 FROM yourTable yt2 WHERE yt.AccountId = yt2.AccountId AND yt.Organization1 = yt2.Organization2)
There are two possible interpretations of your question. The first (where the Organization1 and Organization2 columns are not equal) is trivial:
SELECT AccountID FROM Table WHERE Organization1 <> Organization2
But I suspect you're asking the slightly more difficult interpretation (where Organization2 does not appear in ANY Organization1 value for the same account):
SELECT AccountID From Table T1 WHERE Organization2 NOT IN
(SELECT Organization1 FROM Table T2 WHERE T2.AccountID = T1.AccountID)
Here is a how you could do it:
Test data:
CREATE TABLE #T(rowid int, acc int, org1 int, org2 int)
INSERT #T
SELECT 1,1,10,10 UNION
SELECT 2,1,20,20 UNION
SELECT 3,1,40,30 UNION
SELECT 4,2,10,10 UNION
SELECT 5,2,15,15 UNION
SELECT 6,2,20,20
Then perform a self-join to discover missing org2:
SELECT
*
FROM #T T1
LEFT JOIN
#T T2
ON t1.org1 = t2.org2
AND t1.acc = t2.acc
WHERE t2.org1 IS NULL
SELECT
*
FROM
[YorTable]
WHERE
[Organization1] <> [Organization2] -- The '<>' is read "Does Not Equal".
Use left join as Noel Abrahams presented.