SQL Match exact set of values between two tables - sql

Okay, this is a little hard to explain, but I'll give it my best shot.
I've got two tables, we'll call them table1 and table2. table1 looks something like this:
ID | CampaignID | Package | GroupID
1 | 1 | 1 | 1
2 | 1 | 1 | 2
3 | 1 | 2 | 2
4 | 2 | 1 | 3
5 | 2 | 2 | 3
6 | 2 | 3 | 3
etc
Table2 looks something like this:
ID | ClientID | ClientName | Package | OrderID
1 | 1111 | John Smith | 1 | 155
2 | 1111 | John Smith | 2 | 155
4 | 2222 | Dave Jones | 1 | 177
5 | 2222 | Dave Jones | 2 | 178
6 | 2222 | Dave Jones | 3 | 179
What I'm trying to do, is see if for example, John Smith has any Orders with sets of packages that match one of the Campaign Groups in table1. For the above example, John Smith's order 155 would match CampaignID 1, GroupID 2. Dave Jones's order 177 matches CampaignID 1, GroupID 1. However orders 178 and 179 don't match anything. So each set of packages in an order need to contain all the packages for a given group in order to match it
For the purposes of the select statement I have the client's id along with the orderID, and I'm simply trying to see if the packages in his order match the criteria for any campaigns.
I know I probably haven't explained this too well, so let me know what needs clarifying.
EDIT:
If lets say we search for orderID 155, clientID 1111, then the desired result would be:
CampaignID | GroupID
1 | 2
Perhaps given that GroupID 1 also qualifies, it could return the groupID that qualifies with the largest number of packages.

Is this , what u want?
select table1.CampaignID from Table2
left join table1 on table1.Package =table2.Package
where Table2.ClientID =#ClientID
and Table2.OrderID =#OrderID

I think I figured it out:
SELECT top 1 pcr1.GroupID FROM table1 pcr1
Where Not Exists (Select CampaignID from table1 as pcr2
where CampaignID = #campaignID and pcr2.GroupID= pcr1.GroupID Except
Select t2.Package from table2 as t2 where t2.OrderID = #ordID)
GROUP BY pcr1.GroupID
ORDER BY COUNT(pcr1.GroupID) DESC
Given a known order and a campaign the client is trying to apply for, this gives me the group id that best matches the given order (in regard to packages), if any exists. And just because people seem to be asking, the way the tables appear here aren't exactly the way they're implemented, it's just a simplified equivalent for the purposes of this question.

Here is my Linkedin Article with a more in depth explaination:
https://www.linkedin.com/pulse/profile-matching-mike-inman?lipi=urn%3Ali%3Apage%3Ad_flagship3_profile_view_base_post_details%3Bqj89uO7mTSyVtHet9544VA%3D%3D
CREATE TABLE Campaign(
ID INT,
CampaignID INT,
Package INT,
GroupID INT)
CREATE TABLE ClientOrder(
ID INT,
ClientID INT,
ClientName VARCHAR(20),
Package INT,
OrderID INT)
INSERT Campaign
(ID, CampaignID, Package, GroupID)
VALUES
(1, 1, 1, 1)
,(2, 1, 1, 2)
,(3, 1, 2, 2)
,(4, 2, 1, 3)
,(5, 2, 2, 3)
,(6, 2, 3, 3)
INSERT ClientOrder
(ID, ClientID, ClientName, Package, OrderID)
VALUES
(1, 1111, 'John Smith', 1 , 155)
,(2, 1111, 'John Smith', 2 , 155)
,(4, 2222, 'Dave Jones', 1 , 177)
,(5, 2222, 'Dave Jones', 2 , 178)
,(6, 2222, 'Dave Jones', 3 , 179)
The query:
SELECT CO.ClientName, CampaignID
FROM ClientOrder AS CO
JOIN Campaign as C1
ON CO.Package = C1.Package
GROUP BY CO.ClientName, CampaignID
HAVING COUNT(CO.Package) = (
SELECT COUNT(Package)
FROM Campaign C2
WHERE C1.CampaignID = C2.CampaignID)
Results:
ClientName CampaignID
-------------------- -----------
Dave Jones 1
John Smith 1
Dave Jones 2

Related

Combine First and Last Names of people based on criteria

So I have an interesting scenario. There are 2 events, for which one of the Married EventType I'd like to join associated married couples of the house, grouped by an identifier (HouseID; 2 people in the same house), the EventType, and the EventDate. For the case such as an EventType of Birthday, the 2 residents of the house would not be combined in the same row. In the case of an EventType of Wedding, combine the name result of 2 rows (2 rules below based on LastName) into 1. It's possible to have only 1 person for a HouseID for the EventType of Wedding or Birthday, so therefore they would list as an individual row. The combining rules for EventType of Wedding would be as follows:
If the last names are the same, the FinalName column result would be Will and Mary Stanton
If the last names are different, the FinalName column result would be Stephen Jacobs and Janetsy Lilly.
The combining of the names would be contingent on the HouseID, EventType, and EventDate being the same, specific only to Wedding. This is because it's possible for 2 people to live in a house that are married, but not to each other which we base off the EventDate; we assume the EventDate is the indicator that they are married to each other. The example table input is as follows:
DECLARE #t TABLE (
HouseID INT,
FirstName NVARCHAR(64),
LastName NVARCHAR(64),
EventType NVARCHAR(64),
EventDate DATE
);
INSERT INTO #t (HouseID, FirstName, LastName, EventType, EventDate)
VALUES
(1, 'Will', 'Stanton', 'Birthday', '1974-01-05'),
(1, 'Mary', 'Stanton', 'Birthday', '1980-05-22'),
(2, 'Jason', 'Stockmore', 'Birthday', '1987-12-07'),
(3, 'Mark', 'Mellony', 'Wedding', '2021-04-04'),
(3, 'Stacy', 'Mellony', 'Wedding', '2021-04-04'),
(4, 'Stephen', 'Johnson', 'Wedding', '2012-01-30'),
(4, 'Janetsy', 'Johnson', 'Wedding', '2012-01-30'),
(5, 'George', 'Jackson', 'Wedding', '2009-11-15'),
(5, 'Sally', 'Mistmoore', 'Wedding', '2009-11-15'),
(6, 'Sandy', 'Katz', 'Wedding', '2010-03-19'),
(6, 'Jeff', 'Trilov', 'Wedding', '2016-09-09'),
(7, 'Sandra', 'Kirchbaum', 'Wedding', '2011-05-22'),
(8, 'Jessica', 'Bower', 'Birthday', '1996-02-26'),
(8, 'Frank', 'Fjorn', 'Birthday', '1969-07-19');
The ideal result based on the input table would resemble:
| HouseID | FinalName | EventType | EventDate |
| ------- | ---------------------------------- | --------- | ---------- |
| 1 | Mary Stanton | Birthday | 1974-01-05 |
| 1 | Will Stanton | Birthday | 1980-05-22 |
| 2 | Jason Stockmore | Birthday | 1987-12-07 |
| 3 | Mark and Stacy Mellony | Wedding | 2021-04-04 |
| 4 | Stephen and Janetsy Johnson | Wedding | 2012-01-30 |
| 5 | George Jackson and Sally Mistmoore | Wedding | 2009-11-15 |
| 6 | Sandy Katz | Wedding | 2010-03-19 |
| 6 | Jeff Trilov | Wedding | 2016-09-09 |
| 7 | Sandra Kirchbaum | Wedding | 2011-05-22 |
| 8 | Jessica Bower | Birthday | 1996-02-26 |
| 8 | Frank Fjorn | Birthday | 1969-07-19 |
I have tried a couple of approaches; one of which is using an update statement to update the First Name and update a subsequent FirstName based on Row number using a previously set #Values variable, unioning the result of the types that do not combine (in this case, Birthday). Here is where I built the names then used the MAX() aggregation to select the larger result:
SELECT HouseID,
FirstName
, LastName
, EventType
, EventDate
, RowNum = ROW_NUMBER() OVER (PARTITION BY LastName, EventType ORDER BY 1/0)
, Values1 = CAST(NULL AS VARCHAR(MAX))
INTO #EntityValues1
FROM #t
WHERE EventType = 'Wedding'
UPDATE #EntityValues1
SET #Values1 = Values1 =
CASE WHEN RowNum = 1
THEN FirstName
ELSE #Values1 + ' and ' + FirstName
END
However this example only works with combining FirstName1 + FirstName2 + LastName (in a subsequent query with MAX(Values1) + ' ' + LastName. I had to do a subsequent query to take the approach where I am combining names that do not have the same last name. I know this particular query is a bit tricky, but I'm wondering if there's any magic I'm missing out on. I've seen some suggestions that use a FOR XML approach with STUFF involved, and some other suggestions, but this one appears to be a tough one.
Here is an answer, with a working demo
;WITH
[DoubleWeddings] AS (
SELECT
[HouseID]
FROM
#t
WHERE
[EventType] = 'Wedding'
GROUP BY
[HouseID],
[EventDate]
HAVING
COUNT(*) = 2
),
[DoubleWeddingsSameLastName] AS (
SELECT
T.[HouseID]
FROM
[DoubleWeddings] DW
JOIN
#t T
ON T.[HouseID] = DW.[HouseID]
GROUP BY
T.[HouseID],
T.[LastName]
HAVING
COUNT(*) = 2
),
[DoubleWeddingsDifferentLastName] AS (
SELECT [HouseID] FROM [DoubleWeddings]
EXCEPT
SELECT [HouseID] FROM [DoubleWeddingsSameLastName]
),
[Couples] AS (
SELECT
T.[HouseID],
ROW_NUMBER() OVER (PARTITION BY T.[HouseID] ORDER BY 1/0) [RN],
T.[FirstName],
T.[LastName],
T.[EventType],
T.[EventDate]
FROM
[DoubleWeddings] DW
JOIN
#t T
ON T.[HouseID] = DW.[HouseID]
)
SELECT
DWSL.[HouseID],
FORMATMESSAGE(
'%s and %s %s',
F.[FirstName],
S.[FirstName],
S.[LastName]) [FinalName],
F.[EventType],
F.[EventDate]
FROM
[DoubleWeddingsSameLastName] DWSL
JOIN
[Couples] F
ON F.[HouseID] = DWSL.[HouseID] AND F.[RN] = 1
JOIN
[Couples] S
ON S.[HouseID] = DWSL.[HouseID] AND S.[RN] = 2
UNION ALL
SELECT
DWDL.[HouseID],
FORMATMESSAGE(
'%s %s and %s %s',
F.[FirstName],
F.[LastName],
S.[FirstName],
S.[LastName]) [FinalName],
F.[EventType],
F.[EventDate]
FROM
[DoubleWeddingsDifferentLastName] DWDL
JOIN
[Couples] F
ON F.[HouseID] = DWDL.[HouseID] AND F.[RN] = 1
JOIN
[Couples] S
ON S.[HouseID] = DWDL.[HouseID] AND S.[RN] = 2
UNION ALL
SELECT
T.[HouseID],
FORMATMESSAGE(
'%s %s',
T.[FirstName],
T.[LastName]) [FinalName],
T.[EventType],
T.[EventDate]
FROM
#t T
LEFT JOIN
[DoubleWeddings] DW
ON DW.[HouseID] = T.[HouseID]
WHERE
DW.[HouseID] IS NULL
ORDER BY
[HouseID],
[EventDate],
[FinalName]
There is one issue, I've used the ORDER BY 1/0 technique to skip providing an order for the ROW_NUMBER() which tends to assign the row numbers in the order of the underlying data. However, this is not guaranteed, and could very depending on the parallelization of the query.
It would be better if the order of the combination was provided by a column in the data, however, none is present in the example.
To get this event calendar:
select
t.HouseID,
CONCAT(STRING_AGG(CONCAT(FirstName,
' ',
CASE WHEN ln.LastName<>t.LastName
then t.LastName END),' and '),
' ',
MIN(t.LastName)) as Name,
t.EventType,
t.EventDate
from t
left join (select t1.HouseID, min(t1.LastName) as LastName from t as t1 GROUP BY t1.HouseID having count(t1.LastName)=1) ln on ln.HouseID = t.HouseID
GROUP BY t.EventDate, t.EventType, t.HouseID
order by month(t.EventDate), day(t.EventDate);
output:
HouseID
Name
EventType
EventDate
1
Will Stanton
Birthday
1974-01-05
4
Stephen and Janetsy Johnson
Wedding
2012-01-30
8
Jessica Bower
Birthday
1996-02-26
6
Sandy Katz
Wedding
2010-03-19
3
Mark and Stacy Mellony
Wedding
2021-04-04
7
Sandra Kirchbaum
Wedding
2011-05-22
1
Mary Stanton
Birthday
1980-05-22
8
Frank Fjorn
Birthday
1969-07-19
6
Jeff Trilov
Wedding
2016-09-09
5
George and Sally Jackson
Wedding
2009-11-15
2
Jason Stockmore
Birthday
1987-12-07
see: DBFIDDLE
EDIT:
Corrected the columnname (Name to FinalName), and the ordering of the results.
Fixed the value for HouseId=5
select
t.HouseID,
CONCAT(STRING_AGG(CONCAT(FirstName,
' ',
ISNULL(ln.LastName, t.LastName)
),' and '),
' ',
MIN(ln.LastName)) as FinalName,
t.EventType,
t.EventDate
from t
left join (select t1.HouseID, min(t1.LastName) as LastName from t as t1 GROUP BY t1.HouseID having count(t1.LastName)=1) ln on ln.HouseID = t.HouseID
GROUP BY t.EventDate, t.EventType, t.HouseID
order by HouseID, EventDate
;
see: DBFIDDLE
output:
HouseID
FinalName
EventType
EventDate
1
Will Stanton
Birthday
1974-01-05
1
Mary Stanton
Birthday
1980-05-22
2
Jason Stockmore Stockmore
Birthday
1987-12-07
3
Mark Mellony and Stacy Mellony
Wedding
2021-04-04
4
Stephen Johnson and Janetsy Johnson
Wedding
2012-01-30
5
George Jackson and Sally Mistmoore
Wedding
2009-11-15
6
Sandy Katz
Wedding
2010-03-19
6
Jeff Trilov
Wedding
2016-09-09
7
Sandra Kirchbaum Kirchbaum
Wedding
2011-05-22
8
Frank Fjorn
Birthday
1969-07-19
8
Jessica Bower
Birthday
1996-02-26

Select all records of one table that contain two records in another with certain id

I have two tables of 1:m relation. Need to select which People records have both records in Actions table whit id 1 and 2
People
+----+------+--------------+
| id | name | phone_number |
+----+------+--------------+
| 1 | John | 111111111111 |
+----+------+--------------+
| 3 | Jane | 222222222222 |
+----+------+--------------+
| 4 | Jack | 333333333333 |
+----+------+--------------+
Action
+----+------+------------+
| id | PplId| ActionId |
+----+------+------------+
| 1 | 1 | 1 |
+----+------+------------+
| 2 | 1 | 2 |
+----+------+------------+
| 3 | 2 | 1 |
+----+------+------------+
| 4 | 4 | 2 |
+----+------+------------+
Output
+----+------+--------------+----------
|PplId| name | Phone |ActionId |
+-----+------+-------------+----+-----
| 1 | John | 111111111111| 1 |
+-----+------+-------------+----+-----
| 1 | John | 111111111111| 2 |
+-----+------+-------------+----+-----
Return records of People that have both Have Actionid 1 and Action id 2(Have records in Actions).
Window functions are one method. Assuming actions are not duplicated for a person:
select pa.*
from (select p.*, a.action, count(*) over (partition by p.id) as num_actions
from people p join
action a
on p.id = a.pplid
where a.action in (1, 2)
) pa
where num_actions = 2;
In my opinion, getting two rows with the action detail seems superfluous -- you already know the actions. If you only want the people, then exists comes to mind:
select p.*
from people p
where exists (select 1 from actions where a.pplid = p.id and a.action = 1) and
exists (select 1 from actions where a.pplid = p.id and a.action = 2);
With the right index (actions(pplid, action)), I would expect two exists to be faster than group by.
Try this below query using subquery and join
select a.Pplid, name, phone, actionid from (
select a.pplid as Pplid, name, phone_number as phone
from People P
join Action A on a.pplid= p.id
group by a.pplid, name, phone_number
having count(*)>1 )P
join Action A on a.Pplid= p.Pplid
Try something like this
IF OBJECT_ID('tempdb..#People') IS NOT NULL DROP TABLE #People
CREATE TABLE #People (id INT, name VARCHAR(255), phone_number VARCHAR(50))
INSERT #People
SELECT 1, 'John', '111111111111' UNION ALL
SELECT 3, 'Jane', '222222222222' UNION ALL
SELECT 4, 'Jack', '333333333333'
IF OBJECT_ID('tempdb..#Action') IS NOT NULL DROP TABLE #Action
CREATE TABLE #Action (id INT, PplId INT, ActionId INT)
INSERT #Action
SELECT 1, 1, 1 UNION ALL
SELECT 2, 1, 2 UNION ALL
SELECT 3, 2, 1 UNION ALL
SELECT 4, 4, 2
GO
SELECT p.ID AS PplId
, p.name
, p.phone_number AS Phone
, a.ActionId
FROM #People p
JOIN #Action a
ON p.ID = a.PplId
WHERE p.ID IN ( SELECT PplId
FROM #Action
WHERE ActionId IN (1, 2)
GROUP BY PplId
HAVING COUNT(*) = 2 )
AND a.ActionId IN (1, 2)
GO

SELECT check the colum of the max row

Here my row with my first select:
SELECT
user.id, analytic_youtube_demographic.age,
analytic_youtube_demographic.percent
FROM
`user`
INNER JOIN
analytic ON analytic.user_id = user.id
INNER JOIN
analytic_youtube_demographic ON analytic_youtube_demographic.analytic_id = analytic.id
Result:
---------------------------
| id | Age | Percent |
|--------------------------
| 1 |13-17| 19,6 |
| 1 |18-24| 38.4 |
| 1 |25-34| 22.5 |
| 1 |35-44| 11.5 |
| 1 |45-54| 5.3 |
| 1 |55-64| 1.6 |
| 1 |65+ | 1.2 |
| 2 |13-17| 10 |
| 2 |18-24| 10 |
| 2 |25-34| 25 |
| 2 |35-44| 5 |
| 2 |45-54| 25 |
| 2 |55-64| 5 |
| 1 |65+ | 20 |
---------------------------
The max value by user_id:
---------------------------
| id | Age | Percent |
|--------------------------
| 1 |18-24| 38.4 |
| 2 |45-54| 25 |
| 2 |25-34| 25 |
---------------------------
And I need to filter Age in ['25-34', '65+']
I must have at the end :
-----------
| id |
|----------
| 2 |
-----------
Thanks a lot for your help.
Have tried to use MAX(analytic_youtube_demographic.percent). But I don't know how to filter with the age too.
Thanks a lot for your help.
You can use the rank() function to identify the largest percentage values within each user's data set, and then a simple WHERE clause to get those entries that are both of the highest rank and belong to one of the specific demographics you're interested in. Since you can't use windowed functions like rank() in a WHERE clause, this is a two-step process with a subquery or a CTE. Something like this ought to do it:
-- Sample data from the question:
create table [user] (id bigint);
insert [user] values
(1), (2);
create table analytic (id bigint, [user_id] bigint);
insert analytic values
(1, 1), (2, 2);
create table analytic_youtube_demographic (analytic_id bigint, age varchar(32), [percent] decimal(5, 2));
insert analytic_youtube_demographic values
(1, '13-17', 19.6),
(1, '18-24', 38.4),
(1, '25-34', 22.5),
(1, '35-44', 11.5),
(1, '45-54', 5.3),
(1, '55-64', 1.6),
(1, '65+', 1.2),
(2, '13-17', 10),
(2, '18-24', 10),
(2, '25-34', 25),
(2, '35-44', 5),
(2, '45-54', 25),
(2, '55-64', 5),
(2, '65+', 20);
-- First, within the set of records for each user.id, use the rank() function to
-- identify the demographics with the highest percentage.
with RankedDataCTE as
(
select
[user].id,
youtube.age,
youtube.[percent],
[rank] = rank() over (partition by [user].id order by youtube.[percent] desc)
from
[user]
inner join analytic on analytic.[user_id] = [user].id
inner join analytic_youtube_demographic youtube on youtube.analytic_id = analytic.id
)
-- Now select only those records that are (a) of the highest rank within their
-- user.id and (b) either the '25-34' or the '65+' age group.
select
id,
age,
[percent]
from
RankedDataCTE
where
[rank] = 1 and
age in ('25-34', '65+');

Selecting data from 3 tables, grouping by latest date value and another value

Okay, so I've been racking my brains on this for a while, and I think it's time to ask the collective!
I'm using SQLServer and I've got 3 tables, defined as such:
VolumeData
__________________________
| dataid | currentReading|
--------------------------
| 1 | 22 |
| 7 | 33 |
| 9 | 25 |
| 12 | 12 |
--------------------------
LatestData
________________________________________________________________
| dataid | unitNumber | unitLocation | dateTimeStamp |
----------------------------------------------------------------
| 1 | 2344454 | 2 | 2017-07-10 13:16:29.000 |
| 7 | 2344451 | 44 | 2017-07-10 13:22:29.000 |
| 9 | 2344456 | 92 | 2017-07-10 12:16:29.000 |
| 12 | 2344456 | 12 | 2017-07-10 12:13:23.000 |
----------------------------------------------------------------
unitData
____________________________________________________________________________________
| unitNumber | unitLocation | buildingNumber | officeNumber | officeName | country |
------------------------------------------------------------------------------------
| 2344454 | 2 | 44 | 1 | Telford | UK |
| 2344451 | 44 | 22 | 1 | Telford | UK |
| 2344456 | 92 | 12 | 2 | Hamburg | GER |
| 2344456 | 12 | 33 | 2 | Hamburg | GER |
------------------------------------------------------------------------------------
I need to retrieve just the latest currentReading (based on the dateTimeStamp field in LatestData) along with the following fields, grouped on the unitNumber:
currentReading, unitNumber, officeName, country, buildingNumber
One more thing to note is that records can arrive in any order.
The following is one example that I tried, I've tried many more but I've not kept them open unfortunately:
SELECT
a.currentReading
,MAX(b.dateTimeStamp)
,c.unitNumber
,c.country
,c.officeName
FROM [VolumeData] a INNER JOIN LatestData b ON a.dataid = b.dataid INNER JOIN
unitData c ON c.[unitNumber] = b.[unitNumber] AND c.[unitLocation] = b.[unitLocation];
This results in: Column 'VolumeData.currentReading' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
Any advice would be much appreciated! Everything I try either results in retrieving far too many rows or results in logical SQL errors. I should also add that these tables contain millions of rows, and grow daily, so I'm looking for a really efficient way to do this.
Thanks!
You can use ROW_NUMBER() to order the date. Then you just take the first one, which correspond to the latest date.
SELECT *
FROM (
SELECT a.currentReading
, b.dateTimeStamp
, c.unitNumber
, c.country
, c.officeName
, ROW_NUMBER() OVER (PARTITION BY c.unitNumber ORDER BY b.dateTimeStamp DESC) AS rowNum
FROM [VolumeData] a
INNER JOIN LatestData b ON a.dataid = b.dataid
INNER JOIN unitData c ON c.[unitNumber] = b.[unitNumber] AND c.[unitLocation] = b.[unitLocation]
) a
WHERE rowNum = 1
Same logic as Eric's answer, probably a bit cleaner using CTE and joins lesser records.
DECLARE #VolumeData TABLE
(
dataid int,
currentReading int
);
INSERT INTO #VolumeData VALUES(1, 22);
INSERT INTO #VolumeData VALUES(7, 33);
INSERT INTO #VolumeData VALUES(9, 25);
INSERT INTO #VolumeData VALUES(12,12);
DECLARE #LatestData TABLE
(
dataid int,
unitNumber int,
unitLocation int,
dateTimeStamp datetime
);
INSERT INTO #LatestData VALUES(1, 2344454, 2, '2017-07-10 13:16:29.000');
INSERT INTO #LatestData VALUES(7, 2344451, 44, '2017-07-10 13:22:29.000');
INSERT INTO #LatestData VALUES(9, 2344456, 92, '2017-07-10 12:16:29.000');
INSERT INTO #LatestData VALUES(12, 2344456, 12, '2017-07-10 12:13:23.000');
DECLARE #UnitData TABLE
(
unitNumber int,
unitLocation int,
buildingNumber int,
officeNumber int,
officeName varchar(50),
country varchar(50)
);
INSERT INTO #UnitData VALUES(2344454, 2, 44, 1, 'Telford', 'UK');
INSERT INTO #UnitData VALUES(2344451, 44, 22, 1, 'Telford', 'UK');
INSERT INTO #UnitData VALUES(2344456, 92, 12, 2, 'Hamburg', 'GER');
INSERT INTO #UnitData VALUES(2344456, 12, 33, 2, 'Hamburg', 'GER');
WITH LatestData_CTE (dataid, unitNumber, unitLocation, dateTimeStamp, rowNum)
AS
(
SELECT dataid
, unitNumber
, unitLocation
, dateTimeStamp
, ROW_NUMBER() OVER (PARTITION BY unitNumber ORDER BY dateTimeStamp DESC) AS rowNum
FROM #LatestData
)
SELECT currentReading, l.unitNumber, officeName, country, buildingNumber
FROM LatestData_CTE l
INNER JOIN #VolumeData v ON v.dataid = l.dataid
INNER JOIN #UnitData u ON u.[unitNumber] = l.[unitNumber] AND u.[unitLocation] = l.[unitLocation]
WHERE l.rowNum = 1
Not complete code, but an advice - It can be implemented by ROW_NUMBER function in CTE
Similar to
https://social.msdn.microsoft.com/Forums/sqlserver/en-US/597b876e-eb00-4013-a613-97c377408668/rownumber-and-cte?forum=transactsql
http://datachix.com/2010/02/10/use-a-common-table-expression-and-the-row_number-function-to-eliminate-duplicate-rows-3/
Just google CTE+ROW_NUMBER to get more examples.
So in CTE you join all required tables and you apply ROW_NUMBER over partition, ordered by dateTimestamp (DESC) and then you use WHERE CTE_name.Rank = 1 in the query that uses that CTE.

Self join next timestamp

I am looking to merge timestamp from 2 different row based on Employee and punch card but the max or limit does not work with the from statement, if I only use > then i get every subsequent for everyday... I want the next higher value on a self join, also I have to mention that i have to use SQL 2008! so the lag and Lead does not work!
please help me.
SELECT , Det.name
,Det.[time]
,Det2.[time]
,Det.[type]
,det2.type
,Det.[detail]
FROM [detail] Det
join [detail] Det2 on
Det2.name = Det.name
and
Det2.time > Det.time Max 1
where det.type <>3
Table detail
NAME | Time | Type | detail
john | 10:30| 1 | On
steve| 10:32| 1 | On
john | 10:34| 2 | break
paul | 10:35| 1 | On
steve| 10:45| 3 | Off
john | 10:49| 2 | on
paul | 10:55| 3 | Off
john | 11:12| 3 | Off
Wanted result
John | 10:30 | 10:34 | 1 | 2 | On
John | 10:34 | 10:49 | 2 | 1 | Break
John | 10:49 | 11:12 | 1 | 3 | on
Steve| 10:32 | 10:45 | 1 | 3 | on
Paul | 10:35 | 10:55 | 1 | 3 | On
Thank you in advance!
You can do it with cross apply:
SELECT Det.name
,Det.[time]
,ca.[time]
,Det.[type]
,ca.type
,Det.[detail]
FROM [detail] Det
Cross Apply(Select Top 1 * From detail det2 where det.Name = det2.Name Order By det2.Time) ca
Where det.Type <> 3
As you said LAG or LEAD functions won't work for you, but you could use ROW_NUMBER() OVER (PARTITION BY name ORDER BY time DESC) on both tables and then do a JOIN on RN1 = RN2 + 1
This is just a idea, but I don't see an issue why it shouldn't work.
Query:
;WITH Data (NAME, TIME, type, detail)
AS (
SELECT 'john', CAST('10:30' AS DATETIME2), 1, 'On'
UNION ALL
SELECT 'steve', '10:32', 1, 'On'
UNION ALL
SELECT 'john', '10:34', 2, 'break'
UNION ALL
SELECT 'paul', '10:35', 1, 'On'
UNION ALL
SELECT 'steve', '10:45', 3, 'Off'
UNION ALL
SELECT 'john', '10:49', 2, 'on'
UNION ALL
SELECT 'paul', '10:55', 3, 'Off'
UNION ALL
SELECT 'john', '11:12', 3, 'Off'
)
SELECT t.NAME, LTRIM(RIGHT(CONVERT(VARCHAR(25), t.TIME, 100), 7)) AS time, LTRIM(RIGHT(CONVERT(VARCHAR(25), t2.TIME, 100), 7)) AS time, t.type, t2.type, t.detail
FROM (
SELECT ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY TIME) rn, *
FROM Data
) AS t
INNER JOIN (
SELECT ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY TIME) rn, *
FROM Data
) AS t2
ON t2.NAME = t.NAME
AND t2.rn = t.rn + 1;
Result:
NAME time time type type detail
----------------------------------------------
john 10:30AM 10:34AM 1 2 On
john 10:34AM 10:49AM 2 2 break
john 10:49AM 11:12AM 2 3 on
paul 10:35AM 10:55AM 1 3 On
steve 10:32AM 10:45AM 1 3 On
Any comments, concerns - let me know. :)
As #evaldas-buinauskas said,
The OVER and LAG statements in SQL will work for you.
Here is a similar example:
http://www.databasejournal.com/features/mssql/lead-and-lag-functions-in-sql-server-2012.html