How can I pivot two inner joins - sql

I've been tasked to provide the number of orders completed per Employee for every hour that they have worked for throughout the day. The code that I created provide me that requirement but it is an eye-sore and difficult to understand.
To help management in reading my table I am trying to pivot the results.
I've read the Microsoft example but those don't include examples with joins.
Select
t1.id
,t1.EmpName
,datepart(hour,t1.TimeWorked) as theHour
,CAST(t1.TimeWorked as date) as theDay
,t2.Ready
,Sum(t1.Tot_WT) as AccountsWorked
from Transaction as t1
INNER JOIN
(
SELECT
agent,id,Sum(Ready) as Ready,cast(Endtime as DATE) as theDay,
FROM Login_Data
WHERE [READY] <> [NOT_READY]
GROUP BY agent,agentid,cast(Endtime as DATE)
)as t2
on t1.id = t2.id and t2.theDay = cast(t1.TimeWorked as date)
INNER JOIN
(
Select
id, EmpName
From EmployeeRoster
Where Department in ( 'A','B','C','D')
)as t3
on t1.id = t3.id
Where CAST(t1.TimeWorked as date) = cast(GETDATE() as date)
group by t1.id,t1.EmpName,DATEPART(hour,t1.TimeWorked),CAST(t1.TimeWorked as date)
Honestly the Ready column I can remove.
When executing this logic my results display one row for every hour for each agent
+-----+---------+---------+----------+-------+----------------+
| ID | EmpName | thehour | theday | ready | AccountsWorked |
+-----+---------+---------+----------+-------+----------------+
| 123 | Austin | 7 | 08-20-19 | 16001 | 7 |
+-----+---------+---------+----------+-------+----------------+
What I am trying to achieve with the pivot
+-----+---------+---+---+---+----+----+
| ID | EmpName | 7 | 8 | 9 | 10 | 11 |
+-----+---------+---+---+---+----+----+
| 123 | Austin | 7 | 6 | 9 | 2 | 16 |
+-----+---------+---+---+---+----+----+

You can try below query -
SELECT * FROM
( Select t1.id
,t1.EmpName
,datepart(hour,t1.TimeWorked) as theHour
,CAST(t1.TimeWorked as date) as theDay
,t2.Ready
,Sum(t1.Tot_WT) as AccountsWorked
from Transaction as t1
INNER JOIN ( SELECT agent
,id
,Sum(Ready) as Ready
,cast(Endtime as DATE) as theDay
FROM Login_Data
WHERE [READY] <> [NOT_READY]
GROUP BY agent,agentid,cast(Endtime as DATE))as t2 on t1.id = t2.id
and t2.theDay = cast(t1.TimeWorked as date)
INNER JOIN ( Select id
,EmpName
From EmployeeRoster
Where Department in ( 'A','B','C','D'))as t3 on t1.id = t3.id
Where CAST(t1.TimeWorked as date) = cast(GETDATE() as date)
group by t1.id
,t1.EmpName
,DATEPART(hour,t1.TimeWorked)
,CAST(t1.TimeWorked as date)) AS TEMP
PIVOT(
SUM(AccountsWorked) FOR theHour IN (7,8,9,10,11)
) PVT;

Related

Tie-breaking mutliple matches on MAX() in SQL

I have a table that looks like this:
| client_id | program_id | provider_id | date_of_service | data_entry_date | data_entry_time |
| --------- | ---------- | ----------- | --------------- | --------------- | --------------- |
| 2 | 5 | 6 | 02/02/2022 | 02/02/2022 | 0945 |
| 2 | 5 | 6 | 02/02/2022 | 02/07/2022 | 0900 |
| 2 | 5 | 6 | 02/04/2022 | 02/04/2022 | 1000 |
| 2 | 5 | 6 | 02/04/2022 | 02/04/2022 | 1700 |
| 2 | 5 | 6 | 02/04/2022 | 02/05/2022 | 0800 |
| 2 | 5 | 6 | 02/04/2022 | 02/05/2022 | 0900 |
I need to get the most recent date_of_service entered. From the table above, the desired result/row is:
date_of_service = 02/04/2022, data_entry_date = 02/05/2022, data_entry_time = 0900
This resulting date_of_service will be left joined to the master table.
This query mostly works:
SELECT t1.client_id, t1.program_id, t1.provider_id, t2.date_of_service
FROM table1 as t1
WHERE provider_id = '6'
LEFT JOIN
(SELECT client_id, program_id, provider_id, date_of_service
FROM table2) as t2
ON t2.client_id = t1.client_id
AND t2.program_id = t1.program_id
AND t2.provider_id = t1.provider_id
AND t2.date_of_service =
(SELECT MAX(date_of_service)
FROM t2 as t3
WHERE t3.client_id = t1.client_id
AND t3.program_id = t1.program_id
AND t3.provider_id = t1.provider_id
)
)
But it also returns multiple rows whenever there is more than one match on the max(date_of_service).
To solve this, I need to use the max data_entry_date to break any ties whenever there is more than one row that matches the max(date_of_service). Likewise, I also need to use the max data_entry_time to break any ties whenever there is more than one row that also matches the max data_entry_date.
I tried the following:
SELECT t1.client_id, t1.program_id, t1.provider_id, t2.date_of_service
FROM table1 as t1
WHERE provider_id = '6'
LEFT JOIN
(SELECT TOP(1) client_id, program_id, provider_id, date_of_service, data_entry_date, data_entry_time
FROM table2
ORDER BY date_of_service DESC, data_entry_date DESC, data_entry_time DESC
) as t2
ON t2.client_id = t1.client_id
AND t2.program_id = t1.program_id
AND t2.provider_id = t1.provider_id
But I can only get it to return null values for the date_of_service.
Likewise, this:
SELECT t1.client_id, t1.program_id, t1.provider_id, t2.date_of_service
FROM table1 as t1
WHERE provider_id = '6'
LEFT JOIN
(
SELECT TOP(1) client_id AS client_id2, program_id AS program_id2, provider_id AS provider_id2, date_of_service, data_entry_date, data_entry_time
FROM table2 AS t3
JOIN
(SELECT
MAX(date_of_service) AS max_date_of_service
,MAX(data_entry_date) AS max_data_entry_date
FROM table1
WHERE date_of_service = (SELECT MAX(date_of_service) FROM table2)
) AS t4
ON t3.date_of_service = t4.max_date_of_service
AND t3.data_entry_date = t4.max_data_entry_date
ORDER BY data_entry_time
) AS t2
ON t2.client_id2 = t1.client_id
AND t2.program_id2 = t1.program_id
AND t2.provider_id2 = t1.provider_id
... works (meaning it doesn't throw any errors), but it only seems to return null values for me.
I've tried various combinations of MAX, ORDER BY, and multiple variations of JOIN's, but haven't found one that works yet.
I don't know what version my SQL database is, but it doesn't appear to handle window functions like OVER and PARTITION or other things like COALESCE. I've been using DBeaver 22.2.0 to test the SQL scripts.
Based on your what you've provided, looks like you can simply query table2:
SELECT client_id, program_id, provider_id, MAX(date_of_service), MAX(data_entry_date), MAX(data_entry_time)
FROM table2
GROUP BY client_id, program_id, provider_id
If you need to join this result set to table1, just JOIN to the statement above on client_id, program_id, provider_id
Try using below query. This is using just joins and sub query.
SELECT TOP 1 * FROM table1 t1
JOIN (
SELECT
MAX(date_of_Service) AS Max_date_of_Service
,MAX(data_entry_date) AS Max_data_entry_date
FROM table1
WHERE date_of_Service = (SELECT MAX(date_of_Service) FROM table1)
)t2
ON t1.date_of_Service = t2.Max_date_of_Service
AND t1.data_entry_date = t2.Max_data_entry_date
ORDER BY data_entry_time

pulling rows with a max(column) value but with regards to being smaller than another column in SQL

I have two different data sets but I want to append the columns of one onto the other based on it's date being the max date that is STILL less than the date in the other dataset in SQL.
Example Table 1)
| ID | date | value |
| 05 | 10/13 | ab |
| 10 | 10/15 | sd |
Example Table 2)
| ID2 | date2 | value2 |
| 05 | 10/10 | rf |
| 05 | 10/23 | tx |
| 10 | 10/01 | jk |
| 10 | 10/12 | fr |
| 10 | 10/23 | as |
And the resulting table I want is:
| ID | date | value | date2 | value2 |
| 05 | 10/13 | ab | 10/10 | rf |
| 10 | 10/15 | sd | 10/12 | fr |
When I try to code it, I can't seem to get the correct result. I have tried something like this but I get an error:
select
t1.*
into final
from table1 t1
left join
(select
ID2,
date2,
value2
from
table2
Where max(date2 < date)) AS t2
on t1.ID = t2.ID2;
As noted in the comments, you don't have dates in your sample data. If we pretend you indeed have dates such as those found in this fiddle, then the following could work where CTEs are used.
with max_date as (
select t2.id, max(t2.date) as max_t2_date
from table2 t2
join table1 t1
on t2.id = t1.id
where t2.date < t1.date
group by t2.id
),
max_date_value as (
select t2.id, t2.value, mm.max_t2_date
from table2 t2
join max_date mm
on t2.id = mm.id
and t2.date = mm.max_t2_date
)
select t1.id, t1.date, t1.value, mdv.max_t2_date as date2, mdv.value as value2
from table1 t1
left join max_date_value mdv
on t1.id = mdv.id
Output:
id
date
value
date2
value2
05
2021-10-13T00:00:00.000Z
ab
2021-10-10T00:00:00.000Z
rf
10
2021-10-15T00:00:00.000Z
sd
2021-10-12T00:00:00.000Z
fr
There are likely shorter ways to achieve this but knowing the RDBMS is required. This could at least get you started and play around with different methods.
Assuming that the date and date2 columns are of the type CHAR(5) and stores values like MM/DD, to get the expected results you can use a query like this
WITH t2_rn AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY id2 ORDER BY date2 DESC) AS rn2
FROM table2
WHERE EXISTS (
SELECT 1 FROM table1
WHERE id = id2 AND date > date2
)
)
SELECT
id,
date,
value,
date2,
value2
FROM table1
LEFT JOIN t2_rn ON id = id2 AND rn2 = 1
If you are using SQL Server then you you can use the OUTER APPLY operator, and the query will look like this
SELECT
id,
date,
value,
date2,
value2
FROM table1
OUTER APPLY (
SELECT TOP 1
date2,
value2
FROM table2
WHERE id = id2 AND date > date2 ORDER BY date2 DESC
) t2
Both queries have the same output
id
date
value
date2
value2
5
10/13
ab
10/10
rf
10
10/15
sd
10/12
fr
You can check a working demo with both queries here

SQL Server 2005: How to join table rows only once

I think I've seen answers for similar questions for MySQL, but I'm struggling to find an answer applicable to SQL Server 2005.
So I have a table like this:
| ID | RelationalID | Year
----------------------------
| 1 | A | 2014
| 2 | A | 2014
| 3 | B | 2014
| 4 | A | 2015
| 5 | B | 2015
And I'd like a result like this when I join the same table where RelationID matches but the year is different:
| 2014_ID | 2015_ID | RelationalID |
------------------------------------
| 1 | 4 | A |
| 2 | NULL | A |
| 3 | 5 | B |
But a standard JOIN ends up getting duplicate matches:
| 2014_ID | 2015_ID | RelationalID |
------------------------------------
| 1 | 4 | A |
| 2 | 4 | A |
| 3 | 5 | B |
Is there a way to join two tables where the matches from the right table are joined only once in SQL Server 2005?
I tried this query with no success:
SELECT * FROM myTable
LEFT JOIN (SELECT * FROM myTable) AS t ON t.RelationalID = myTable.RelationalID
WHERE myTable.Year = 2014 and t.Year = 2015
You can get the result based on ROW_NUMBERs, but you need a rule how to assign them, I assumed it's based on the Id.
;WITH cte AS
(SELECT Id,
RelationalId,
year,
row_number()
over (partition by RelationalId, year
order by Id) as rn
FROM [YourTable]
)
select t1.id as Id_2014,t2.id as Id_2015, t1.RelationalId
from cte as t1 left join cte as t2
on t1.RelationalId = t2.RelationalId
and t1.rn = t2.rn
and t2.year = 2015
where t1.Year = 2014
This is based on TMNT2014's fiddle
Below Sql would give you the result you are looking for but as I said before complexity would depend on the original set of data you have in your table. Here is the SQL Fiddle - http://sqlfiddle.com/#!3/d6300/24 - Good Luck!
;WITH CTE_Union AS
(SELECT
a.Id AS Id2014,
NULL AS Id2015,
a.RelationalId
FROM [YourTable] a
WHERE a.Year = 2014
UNION
SELECT
NULL AS Id2014,
b.Id AS Id2015,
b.RelationalId
FROM [YourTable] b
WHERE b.Year = 2015)
SELECT Distinct CASE WHEN Id2014 IS NULL THEN (SELECT MIN(Id2014) FROM CTE_Union C WHERE C.RelationalId =M.RelationalId) ELSE Id2014 END AS ID2014 ,
CASE WHEN Id2015 IS NULL AND Id2014 = (SELECT MIN(Id2014) FROM CTE_Union C2 WHERE C2.RelationalId =M.RelationalId) THEN (SELECT MIN(Id2015) FROM CTE_Union C WHERE C.RelationalId =M.RelationalId) ELSE Id2015 END
,RelationalID
FROM CTE_Union M
DECLARE #MyTable TABLE
(
ID INT,
RelationalID VARCHAR(10),
[Year] INT
)
INSERT INTO #MyTable
VALUES
( 1 ,'A', 2014),
( 2 ,'A', 2014),
( 3 ,'B', 2014),
( 4 ,'A', 2015),
( 5 ,'B', 2015)
;WITH TEST AS
(
SELECT
a.Id AS Id2014,
NULL AS Id2015,
a.RelationalId,
RANK() OVER (PARTITION BY RelationalId ORDER BY ID) Ranked
FROM #MyTable a
WHERE a.Year = 2014
UNION
SELECT
NULL AS Id2014,
b.Id AS Id2015,
b.RelationalId,
RANK() OVER (PARTITION BY RelationalId ORDER BY ID) Ranked
FROM #MyTable b
WHERE b.Year = 2015
)
SELECT
t1.Id2014,
t2.Id2015,
t1.RelationalID
FROM TEST t1
LEFT JOIN TEST t2
ON t1.Ranked = t2.Ranked
AND t1.RelationalID = t2.RelationalID
AND t2.Id2015 IS NOT NULL
WHERE t1.Id2014 IS NOT NULL
ORDER BY t1.Id2014
I used a union and then ranked each side by relational id and left joined them.
Here is the output:
Id2014 Id2015 RelationalID
1 4 A
2 NULL A
3 5 B
There are probably a few ways to solve this but below shows an example of utilizing "Derived Tables" in a query.
SELECT
q1.Id AS [2014_Id],
q2.Id AS [2015_Id],
q1.RelationalId
FROM (SELECT
MAX(a.Id) AS Id,
a.RelationalId
FROM [table] a
WHERE a.Year = 2014
GROUP BY
a.RelationalId) q1
INNER JOIN (SELECT
MAX(b.Id) AS Id,
b.RelationalId
FROM [table] b
WHERE b.Year = 2015
GROUP BY
b.RelationalId) q2
ON q2.RelationalId = q1.RelationalId

MSSQL: Only last entry in GROUP BY (with id)

Following / copying computhomas's question, but adding some twists...
I have the following table in MSSQL2008
id | business_key | result | date
1 | 1 | 0 | 9
2 | 1 | 1 | 8
3 | 2 | 1 | 7
4 | 3 | n | 6
5 | 4 | 1 | 5
6 | 4 | 0 | 4
And now i want to group based on the business_key returning the complete entry with the newest date.
So my expected result is:
id | business_key | result | date
1 | 1 | 0 | 9
3 | 2 | 1 | 7
4 | 3 | n | 6
5 | 4 | 1 | 5
I also bet that there is a way to achieve that, i just can't find / see / think of it at the moment.
edit: sorry about this, I actually meant something else from original question I did. I felt like editing this might be better than accepting a solution and making another question. my original problem was that I am not filtering by id.
SELECT t.*
FROM
(
SELECT *, ROW_NUMBER() OVER
(
PARTITION BY [business_key]
ORDER BY [date] DESC
) AS [RowNum]
FROM yourTable
) AS t
WHERE t.[RowNum] = 1
SELECT
*
FROM
mytable
WHERE
ID IN (SELECT MAX(ID) FROM mytable GROUP BY business_key)
SELECT
MAX(T1.id) AS [id],
T1.business_key,
T1.result
FROM
dbo.My_Table T1
LEFT OUTER JOIN dbo.My_Table T2 ON
T2.business_key = T1.business_key AND
T2.id > T1.id
WHERE
T2.id IS NULL
GROUP BY T1.business_key,
T1.result
ORDER BY MAX(T1.id)
Edited based on clarifications
SELECT M1.*
FROM My_Table M1
INNER JOIN
(
SELECT [business_key], MAX([date]) as MaxDate
FROM My_Table
GROUP BY [business_key]
) M2 ON M1.business_key = M2.business_key AND M1.[date] = M2.MaxDate
ORDER BY M1.[id]
Assuming the combination of business_key & date is unique then....
Working example (3rd time is a charm):
declare #src as table(id int, business_key int,result int,[date] int)
insert into #src
SELECT 1,1,0,9
UNION SELECT 2,1,1,8
UNION SELECT 3,2,1,7
UNION SELECT 4,3,1,6
UNION SELECT 5,4,1,5
UNION SELECT 6,4,0,4
;with bkdate(business_key,[date])
AS
(
select business_key,MAX([date])
from #src
group by business_key
)
select src.* from #src src
inner join bkdate
ON src.[date] = bkdate.date
and src.business_key = bkdate.business_key
order by id
How about (edited after question change):
with latestdate as (
select business_key, maxdate=max(date)
from the_table
group by business_key
), latest as (
select ID = max(id)
from the_table
inner join latestdate
on the_table.business_key=latestdate.business_key
and the_table.date=latestdate.maxdate
group by the_table.business_key
)
select the_table.*
from the_table
inner join latest
on latest.id=the_table.id

mysql query with group by and order by

I have a table like this:
.--------------------------------.
| Name | XX | Date |
.--------------------------------.
| N1 | 5 | 2009-05-01 |
| N1 | 10 | 2008-01-08 |
| N2 | 8 | 2009-02-02 |
.________________________________.
My result should be this:
.------------.
| Name | XX |
.------------.
| N1 | 5 |
| N2 | 8 |
.____________.
I just want the rows, grouped by Name but just the newest ones. I dont want the row with XX=10 in this example because the date 2009-05-01 > 2008-01-08.
I don't know how to sort by Date and group it :(
What do you want the grouping for?
select Name, XX
from yourTable t1
where Date = (select MAX(Date)
from yourTable t2
where t1.Name = t2.Name)
If you need to sum XX for the last day, then:
select Name, SUM(XX)
from yourTable t1
where Date = (select MAX(Date)
from yourTable t2
where t1.Name = t2.Name)
group by Name
SELECT Name, XX FROM myTable GROUP BY Name ORDER BY Date DESC
You may need to put quotes around your date field
`Date`
Sorry but these 2 are both wrong.
You should
SELECT Name, XX
FROM t1
JOIN (SELECT Name, max(date)
FROM t1
GROUP BY Name) subquery
ON t1.Name = subquery.Name AND t1.Date = subquery.Date