How to merge two query results joining same date - sql

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

Related

SQL: return rows with only the earliest date for each id but only if it satisfies condition

I would like to get list of unique id that have 'condition=1' before 'condition=2'.
id
date
condition1
condition2
1
2022/02
1
0
1
2022/04
0
1
1
2022/05
0
0
2
2021/09
0
1
2
2022/01
1
0
3
2022/02
1
0
3
2022/05
0
1
In this case it would be 1 and 3.
SELECT id, MIN(date) FROM TABLE GROUP BY id
I know that i can do something like this to get first dates for id but i just cant figure out what to do for my problem
We can GROUP BY id and build two conditional MIN dates using CASE WHEN.
In the HAVING clause we say that the minimum date with condition 1 must appear before the minimum date with condition 2.
SELECT id
FROM yourtable
GROUP BY id
HAVING MIN(CASE WHEN condition1 = 1 THEN date END) <
MIN(CASE WHEN condition2 = 1 THEN date END)
ORDER BY id;
Try out here: db<>fiddle
Something like:
SELECT DISTINCT Id
FROM
(SELECT id, MIN(date)
FROM TheTable
WHERE Condition1 = 1
GROUP BY Id) c1
INNER JOIN
(SELECT Id, MIN(date)
FROM TheTable
WHERE Condition2 = 1
GROUP BY Id) c2
ON c1.Id=C2.Id AND c1.Date < c2.Date

Checking if all values for user_id IS NOT NULL

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

SQL Select Distinct Records From Two Tables

I am trying to write a SQL statement that will return a set of Distinct set of CompanyNames from a table based on the most recent SaleDate withing a specified date range from another table.
T01 = Account
T02 = TransHeader
The fields of importance are:
T01.ID, T01.CompanyName
T02.AccountID, T02.SaleDate
T01.ID = T02.AccountID
What I want to return is the Max SaleDate for each CompanyName without any duplicate CompanyNames and only the Max(SaleDate) as LastSale. I will be using a Where Clause to limit the SaleDate range.
I tried the following but it returns all the records for all SalesDates in the range. This results in the same company being listed multiple times.
Current MS-SQL Query
SELECT T01.CompanyName, T02.LastSale
FROM
(SELECT DISTINCT ID, IsActive, ClassTypeID, CompanyName FROM Account) T01
FULL OUTER JOIN
(SELECT DISTINCT AccountID, TransactionType, MAX(SaleDate) LastSale FROM TransHeader group by AccountID, TransactionType, SaleDate) T02
ON T01.ID = T02.AccountID
WHERE ( ( T01.IsActive = 1 )AND
( (Select Max(SaleDate)From TransHeader Where AccountID = T01.ID AND TransactionType in (1,6) AND SaleDate is NOT NULL)
BETWEEN '01/01/2016' AND '12/31/2018 23:59:00' AND (Select Max(SaleDate)From TransHeader Where AccountID = T01.ID AND TransactionType in (1,6) AND SaleDate is NOT NULL) IS NOT NULL
)
)
ORDER BY T01.CompanyName
I thought the FULL OUTER JOIN was the ticket but it did not work and I am stuck.
Sample data Account Table (T01)
ID CompanyName IsActive ClassTypeID
1 ABC123 1 1
2 CDE456 1 1
3 EFG789 1 1
4 Test123 0 1
5 Test456 1 1
6 Test789 0 1
Sample data Transheader table (T02)
AccountID TransactionType SaleDate
1 1 02/03/2012
2 1 03/04/2013
3 1 04/05/2014
4 1 05/06/2014
5 1 06/07/2014
6 1 07/08/2015
1 1 08/09/2016
1 1 01/15/2016
2 1 03/20/2017
2 1 03/21/2017
3 1 03/04/2017
3 1 04/05/2018
3 1 05/27/2018
4 1 06/01/2018
5 1 07/08/2018
5 1 08/01/2018
5 1 10/11/2018
6 1 11/30/2018
Desired Results
CompanyName LastSale (Notes note returned in the result)
ABC123 01/15/2016 (Max(SaleDate) LastSale for ID=1)
CDE456 03/21/2017 (Max(SaleDate) LastSale for ID=2)
EFG789 05/27/2018 (Max(SaleDate) LastSale for ID=3)
Testing456 10/11/2018 (Max(SaleDate) LastSale for ID=5)
ID=4 & ID=6 are note returned because IsActive = 0 for these records.
One option is to select the maximum date in the select clause.
select
a.*,
(
select max(th.saledate)
from transheader th
where th.accountid = a.id
and th.saledate >= '2016-01-01'
and th.saledate < '2019-01-01'
) as max_date
from account a
where a.isactive = 1
order by a.id;
If you only want to show transaction headers with sales dates in the given date range, then you can just inner join the maximum dates with the accounts. In order to do so, you must group your date aggregation per account:
select a.*, th.max_date
from account a
join
(
select accountid, max(saledate) as max_date
from transheader
and saledate >= '2016-01-01'
and saledate < '2019-01-01'
group by accountid
) th on th.accountid = a.id
where a.isactive = 1
order by a.id;
select CompanyName,MAX(SaleDate) SaleDate from Account a
inner join Transheader b on a.id = b.accountid
group by CompanyName
order by 1

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;

Checking if the row has the max value in a group

I'm trying get to find out if a row has the max value in a group. Here's really simple example:
Data
VoteCount LocationId UserId
3 1 1
4 1 2
3 2 2
4 2 1
Pseudo-query
select
LocationId,
sum(case
when UserId = 1 /* and has max vote count*/
then 1 else 0
end) as IsUser1Winner,
sum(case
when UserId = 2 /* and has max vote count*/
then 1 else 0
end) as IsUser2Winner
from LocationVote
group by LocationID
It should return:
LocationId IsUser1Winner IsUser2Winner
1 0 1
2 1 1
I also couldn't find a way to generate dynamic column names here. What would be the simplest way to write this query?
You could also do this using a Case statement
WITH CTE as
(SELECT
MAX(VoteCount) max_votes
, LocationId
FROM LocationResult
group by LocationId
)
SELECT
A.LocationId
, Case When UserId=1
THEN 1
ELSE 0
END IsUser1Winner
, Case when UserId=2
THEn 1
ELSE 0
END IsUser2Winner
from LocationResult A
inner join
CTE B
on A.VoteCount = B.max_votes
and A.LocationId = B.LocationId
Try this:
select *
from table t
cross apply (
select max(votes) max_value
from table ref
where ref.group = t.group
)votes
where votes.max_value = t.votes
but if your table is huge and has no propriate indexes performance may be poor
Another way is to get max values by groups into table variable or temp table and then join it to original table.