SELECT data from another table with max date - sql

I need help with a SELECT query in which I need to join to another table and get record with max date. I have created sample to demonstrate. My last SELECT is incorrect and I need guidance to fix it or if there is a better way in Sql server 2014
CREATE TABLE #EmpTable
(
EmpNum INT,
colA VARCHAR(5) NULL,
colB VARCHAR(5) NULL
)
CREATE TABLE #EmpDetailTbl
(
EmpNum INT,
Name VARCHAR(10),
Department VARCHAR(10) NULL,
ReportDate DATETIME NOT NULL
)
INSERT INTO #EmpTable
VALUES (101, 'val11', 'Val21'), (102, 'val12', 'Val21'), (103, 'val13', 'Val23');
INSERT INTO #EmpDetailTbl
VALUES (101, 'emp101', 'Dept1', '05/01/2018'), (101, 'emp101', 'Dept2', '06/01/2018'),
(101, 'emp101', 'Dept1', '05/01/2017'), (102, 'emp102', 'Dept3', '04/01/2018'),
(102, 'emp102', 'Dept1', '05/01/2018')
--select * from #EmpDetailTbl
--select * from #EmpTable
SELECT
a.EmpNum, Name, ColA, ColB, Department
FROM
#EmpTable a
LEFT OUTER JOIN
#EmpDetailTbl b ON a.EmpNum = b.EmpNum
AND ReportDate = (SELECT MAX(ReportDate)
FROM #EmpDetailTbl
a.EmpNum = b.EmpNum)

Use rank() analytic function to enumerate rows based on their report dates and then pick only the first for each employee:
SELECT EmpNum, Name, ColA, ColB, Department
FROM (
SELECT
a.EmpNum, b.Name, a.ColA, a.ColB, b.Department,
rank() over (partition by a.EmpNum order by b.ReportDate desc) as rn
FROM #EmpTable a
LEFT JOIN #EmpDetailTbl b ON
a.EmpNum = b.EmpNum
) t
WHERE rn = 1;
This query will take care of ties: in case there is more than 1 record for employee with the same date which also happens to be maximum it will show them all.

If I understand correctly you can try this to get MaxDate row.
using a exists to get MAX(b1.ReportDate) by EmpNum
select a.EmpNum, Name, ColA, ColB, Department
FROM #EmpTable a
LEFT JOIN #EmpDetailTbl b on a.EmpNum = b.EmpNum
WHERE exists (
SELECT 1
FROM #EmpDetailTbl b1
WHERE b1.EmpNum = b.EmpNum
GROUP BY b1.EmpNum
HAVING MAX(b1.ReportDate) = b.ReportDate
)
sqlfiddle: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=a3deade95dbd25d9cadadee37e16d9c6
Result
EmpNum Name ColA ColB Department
101 emp101 val11 Val21 Dept2
102 emp102 val12 Val21 Dept1
103 emp103 val13 Val23 Dept1

ReportDate SHOULD BE a.ReportDate

Related

How to fetch records from two tables without common column using CTE

Users table details
userid values (abc,xyz,abc,sdf)
master table details
(mid,priority)values(101,1),(102,2),(101,1),(103,1)
i need to count of mid based on userid (userid is names of users) group by priority(priority is int ) grouping like case priority =1 then 'Open', priority =2 then 'closed' etc using CTE(common table expression)
Select * from users
userid
abc
xyz
abc
sdf
Select * from master
mid Priority
101 1
102 2
101 1
103 1
(Priority 1= Open 2=Closed)
OUTPUT expected:
Userid count(mid) Priority
abc 2 Open
xyz 1 Closed
sdf 1 Open
Try this:
use db_test;
go
drop table dbo.users;
create table dbo.users
(
userid varchar(max) not null
)
;
insert into dbo.users
values
('abc'),
('xyz'),
('sdf')
create table dbo.master
(
mid int not null,
Priority int not null
)
;
insert into dbo.master
values
(101, 1),
(102, 2),
(101, 1),
(103, 1)
;
with cte1 as (
select userid, row_number() over(order by userid asc) as rn
from dbo.users
), cte2 as (
select mid, priority, dense_rank() over(order by mid asc) as rn
from dbo.master
)
select a.userid, count(*) as [count(mid)], b.priority
from cte1 a join cte2 b on a.rn = b.rn
group by a.userid, b.priority

Find most recent record by date

This is my original data (anonymised):
id usage verified date
1 4000 Y 2015-03-20
2 5000 N 2015-06-20
3 6000 N 2015-07-20
4 7000 Y 2016-09-20
Original query:
SELECT
me.usage,
mes.verified,
mes.date
FROM
Table1 me,
Table2 mes,
Table3 m,
Table4 mp
WHERE
me.theFk=mes.id
AND mes.theFk=m.id
AND m.theFk=mp.id
How would I go about selecting the most recent verified and non-verified?
So I would be left with:
id usage verified date
1 6000 N 2015-07-20
2 7000 Y 2016-09-20
I am using Microsoft SQL Server 2012.
First, do not use implicit joins. This was discontinued more than 10 years ago.
Second, embrace the power of the CTE, the in clause and row_number:
with CTE as
(
select
me.usage,
mes.verified,
mes.date,
row_number() over (partition by Verified order by Date desc) as CTEOrd
from Table1 me
inner join Table2 mes
on me.theFK = mes.id
where mes.theFK in
(
select m.id
from Table3 m
inner join Table4 mp
on mp.id = m.theFK
)
)
select CTE.*
from CTE
where CTEOrd = 1
You can select the TOP 1 ordered by date for verified=N, union'd with the TOP 1 ordered by date for verified=Y.
Or in pseudo SQL:
SELECT TOP 1 ...fields ...
FROM ...tables/joins...
WHERE Verified = 'N'
ORDER BY Date DESC
UNION
SELECT TOP 1 ...fields ...
FROM ...tables/joins...
WHERE Verified = 'Y'
ORDER BY Date DESC
drop table #stack2
CREATE TABLE #stack2
([id] int, [usage] int, [verified] varchar(1), [date] datetime)
;
INSERT INTO #stack2
([id], [usage], [verified], [date])
VALUES
(1, 4000, 'Y', '2015-03-20 00:00:00'),
(2, 5000, 'N', '2015-06-20 00:00:00'),
(3, 6000, 'N', '2015-07-20 00:00:00'),
(4, 7000, 'Y', '2016-09-20 00:00:00')
;
;with cte as (select verified,max(date) d from #stack2 group by verified)
select row_number() over( order by s2.[verified]),s2.[usage], s2.[verified], s2.[date] from #stack2 s2 join cte c on c.verified=s2.verified and c.d=s2.date
As per the data shown i had written the query.
for your scenario this will be use full
WITH cte1
AS (SELECT me.usage,
mes.verified,
mes.date
FROM Table1 me,
Table2 mes,
Table3 m,
Table4 mp
WHERE me.theFk = mes.id
AND mes.theFk = m.id
AND m.theFk = mp.id),
cte
AS (SELECT verified,
Max(date) d
FROM cte1
GROUP BY verified)
SELECT Row_number()
OVER(
ORDER BY s2.[verified]),
s2.[usage],
s2.[verified],
s2.[date]
FROM cte1 s2
JOIN cte c
ON c.verified = s2.verified
AND c.d = s2.date
You can as the below Without join.
-- Mock data
DECLARE #Tbl TABLE (id INT, usage INT, verified CHAR(1), date DATETIME)
INSERT INTO #Tbl
VALUES
(1, 4000 ,'Y', '2015-03-20'),
(2, 5000 ,'N', '2015-06-20'),
(3, 6000 ,'N', '2015-07-20'),
(4, 7000 ,'Y', '2016-09-20')
SELECT
A.id ,
A.usage ,
A.verified ,
A.MaxDate
FROM
(
SELECT
id ,
usage ,
verified ,
date,
MAX(date) OVER (PARTITION BY verified) MaxDate
FROM
#Tbl
) A
WHERE
A.date = A.MaxDate
Result:
id usage verified MaxDate
----------- ----------- -------- ----------
3 6000 N 2015-07-20
4 7000 Y 2016-09-20
CREATE TABLE #Table ( ID INT ,usage INT, verified VARCHAR(10), _date DATE)
INSERT INTO #Table ( ID , usage , verified , _date)
SELECT 1,4000 , 'Y','2015-03-20' UNION ALL
SELECT 2, 5000 , 'N' ,'2015-06-20' UNION ALL
SELECT 3, 6000 , 'N' ,'2015-07-20' UNION ALL
SELECT 4, 7000 , 'Y' ,'2016-09-20'
SELECT ROW_NUMBER() OVER(ORDER BY usage) ID,usage , A.verified , A._date
FROM #Table
JOIN
(
SELECT verified , MAX(_date) _date
FROM #Table
GROUP BY verified
) A ON #Table._date = A._date

self joining sql without aggregation

I have a table with the below structure:
ID EmployeeType Name
1 Contract John, Baxter
2 Contract Will, Smith
3 Full Josh, Stevens
4 Full Sitar, Zhang
All I need to do is Pivot it so I get the below output:
Contract_Employee FullTime_Employee
John, Baxter Josh, Stevens
Will,Smith Sitar, Zhang
Any idea how I can do this in one query?
That's kind of a funny request. Here's how I would do it:
(basically, just deriving a fake key to "join" on for two derived tables, on for contractors, one for employees)
CREATE TABLE #Table1
([ID] int, [EmployeeType] varchar(8), [Name] varchar(13))
;
INSERT INTO #Table1
([ID], [EmployeeType], [Name])
VALUES
(1, 'Contract', 'John, Baxter'),
(2, 'Contract', 'Will, Smith'),
(3, 'Full', 'Josh, Stevens'),
(4, 'Full', 'Sitar, Zhang'),
(5, 'Full','Bob, Bob'),
(6, 'Contract','Bob, Bob')
;
select
c.name as ContractEmployee,
f.name as FullTime_Employee
from
(
select
row_number() over (order by id) as RN,
name
from
#table1
where
employeetype = 'Contract'
) c
full join (
select
row_number() over (order by id) as RN,
name
from
#table1
where
employeetype = 'Full'
) f
on
c.name = f.name OR
c.rn = f.rn
One method of doing this is to use aggregation:
select max(case when employeetype = 'Contract' then Name end) as ContractEmployees,
max(case when employeetype = 'Full' then Name end) as FullEmployees
from (select t.*,
row_number() over (partition by employeetype order by id) as seqnum
from table t
) t
group by seqnum;

how to insert many records excluding some

I want to create a table with a subset of records from a master table.
for example, i have:
id name code
1 peter 73
2 carl 84
3 jack 73
I want to store peter and carl but not jack because has same peter's code.
I need hight performance because i have 20M records.
I try this:
SELECT id, name, DISTINCT(code) INTO new_tab
FROM old_tab
WHERE (conditions)
but don't work.
Assuming you want to pick the row with the maximum id per code, then this should do it:
insert into new_tab (id, name, code)
(SELECT id, name, code
FROM
(
SELECT id, name, code, rank() as rnk OVER (PARTITION BY code ORDER BY id DESC)
FROM old_tab WHERE rnk = 1
)
)
and for the minimum id per code, just change the sort order in the rank from DESC to ASC:
insert into new_tab (id, name, code)
(SELECT id, name, code
FROM
(
SELECT id, name, code, rank() as rnk OVER (PARTITION BY code ORDER BY id ASC)
FROM old_tab WHERE rnk = 1
)
)
Using a derived table, you can find the minID for each code, then join back to that in the outer to get the rest of the columns for that ID from oldTab.
select id,name,code
insert into newTabFROM
from old_tab t inner join
(SELECT min(id) as minId, code
from old_tab group by code) x
on t.id = x.minId
WHERE (conditions)
Try this:
CREATE TABLE #Temp
(
ID INT,
Name VARCHAR(50),
Code INT
)
INSERT #Temp VALUES (1, 'Peter', 73)
INSERT #Temp VALUES (2, 'Carl', 84)
INSERT #Temp VALUES (3, 'Jack', 73)
SELECT t2.ID, t2.Name, t2.Code
FROM #Temp t2
JOIN (
SELECT t.Code, MIN(t.ID) ID
FROM #temp t
JOIN (
SELECT DISTINCT Code
FROM #Temp
) d
ON t.Code = d.Code
GROUP BY t.Code
) b
ON t2.ID = b.ID

SQL Server Distinct Question

I need to be able to select only the first row for each name that has the greatest value.
I have a table with the following:
id name value
0 JOHN 123
1 STEVE 125
2 JOHN 127
3 JOHN 126
So I am looking to return:
id name value
1 STEVE 125
2 JOHN 127
Any idea on the MSSQL Syntax on how to perform this operation?
While you specified SQL Server, you did not specify the version. If you are using SQL Server 2005 or later, you can do something like:
With RankedItems As
(
Select id, name, value
, Row_Number() Over ( Partition By name Order By value Desc, id Asc ) As ItemRank
From Table
)
Select id, name, value
From RankedItems
Where ItemRank = 1
try:
SELECT
MIN(id) as id,dt.name,dt.value
FROM (SELECT
name,MAX(value) as value
FROM YourTable
GROUP BY name
) dt
INNER JOIN YourTable t ON dt.name=t.name and dt.value=t.value
GROUP BY dt.name,dt.value
try it out:
DECLARE #YourTable table (id int, name varchar(10), value int)
INSERT #YourTable VALUES (0, 'JOHN', 123)
INSERT #YourTable VALUES (1, 'STEVE', 125)
INSERT #YourTable VALUES (2, 'JOHN', 127)
INSERT #YourTable VALUES (3, 'JOHN', 126)
--extra data not in the question, shows why you need the outer group by
INSERT #YourTable VALUES (4, 'JOHN', 127)
INSERT #YourTable VALUES (5, 'JOHN', 127)
INSERT #YourTable VALUES (6, 'JOHN', 127)
INSERT #YourTable VALUES (7, 'JOHN', 127)
SELECT
MIN(id) as id,dt.name,dt.value
FROM (SELECT
name,MAX(value) as value
FROM #YourTable
GROUP BY name
) dt
INNER JOIN #YourTable t ON dt.name=t.name and dt.value=t.value
GROUP BY dt.name,dt.value
ORDER BY id
output:
id name value
----------- ---------- -----------
1 STEVE 125
2 JOHN 127
(2 row(s) affected)
You could do something like
SELECT id, name, value
FROM (SELECT id, name, value
ROWNUMBER() OVER (PARTITION BY name ORDER BY value DESC) AS r
FROM table) AS x
WHERE x.r = 1 ;
This will not work in SQL Server 2000 and earlier, but it will be incredibly fast in SQL Server 2005 and 2008
How about:
SELECT a.id, a.name, b.maxvalue
FROM mytbl a
INNER JOIN (SELECT id, max(value) as maxvalue
FROM mytbl
GROUP BY id) b ON b.id = a.id
SELECT a.id, a.name, a.value
FROM mytbl a
INNER JOIN (SELECT name, max(value) as maxvalue
FROM mytbl
GROUP BY name) b ON b.name = a.name and b.maxvalue = a.value