Merge two rows with condition SQL View - sql

I have a View which has a SQL Script as:
Select
a.iAssetId,
ac.eEventCode,
vm.dtUTCDateTime,
g.iGeofenceId,
g.sGeofenceName,
c.sCategoryName,
c.iCategoryId,
s.sSiteName,
s.iSiteId,
CASE WHEN ac.eEventCode = 6 THEN vm.dtUTCDateTime ELSE NULL END as EnterTime,
CASE WHEN ac.eEventCode = 7 THEN vm.dtUTCDateTime ELSE NULL END as ExitTime,
CASE WHEN
a.iAssetId = Lead(a.iAssetId) OVER (ORDER BY a.iAssetId)
AND g.iGeofenceId = Lead(g.iGeofenceId) OVER (ORDER BY a.iAssetId)
AND ac.eEventCode != Lead(ac.eEventCode) OVER (ORDER BY a.iAssetId)
THEN DATEDIFF(minute, vm.dtUTCDateTime, Lead(vm.dtUTCDateTime) OVER (ORDER BY a.iAssetId)) ELSE NULL END as Test
From AssetCommunicationSummary ac
Inner join VehicleMonitoringLog vm on vm.iVehicleMonitoringId = ac.iVehicleMonitoringId
Inner Join Geofences g on g.iGeofenceId = vm.iGeofenceId
Inner Join Assets a on a.iAssetId = ac.iAssetId
Inner Join Categories c on c.iCategoryId = a.iCategoryId
Inner Join Sites s on s.iSiteId = c.iSiteId
Where ac.eEventCode = 6 OR ac.eEventCode = 7
Group by
a.iAssetId,
ac.eEventCode,
vm.dtUTCDateTime,
g.iGeofenceId,
g.sGeofenceName,
c.sCategoryName,
c.iCategoryId,
s.sSiteName,
s.iSiteId
I have used Lead to calculate the Time differenc in minutes for leading rows based on conditions.
I need to now merge the leading Row and the Current Row based on Condition.
Is there a possible way to do this?
The goal is to get the EnterTime and ExitTime in the Same Row with Time Difference in the Column Next to it.
My result is like this:

If your eventcode is always going to be 6 and 7, then you can just join to that table twice using that clause in the join itself. I think I've got the rest of your schema joined up properly below, but if not, you can adjust it around to fit.
Select
a.iAssetId,
vmEnter.dtUTCDateTime,
g.iGeofenceId,
g.sGeofenceName,
c.sCategoryName,
c.iCategoryId,
s.sSiteName,
s.iSiteId,
vmEnter.dtUTCDateTime as EnterTime,
vmExit.dtUTCDateTime as ExitTime,
DATEDIFF(minute, vmEnter.dtUTCDateTime, vmExit.dtUTCDateTime) as ExitTime,
From Sites s
Inner Join Categories c on s.iSiteId = c.iSiteId
Inner Join Assets a on c.iCategoryId = a.iCategoryId
Inner Join AssetCommunicationSummary acEnter on a.iAssetId = acEnter.iAssetId and acEnter.eEventCode = 6
Inner Join VehicleMonitoringLog vmEnter on vmEnter.iVehicleMonitoringId = acEnter.iVehicleMonitoringId
Inner Join AssetCommunicationSummary acExit on a.iAssetId = acExit.iAssetId and acExit.eEventCode = 7
Inner Join VehicleMonitoringLog vmExit on vmExit.iVehicleMonitoringId = acExit.iVehicleMonitoringId
Inner Join Geofences g on g.iGeofenceId = vmEnter.iGeofenceId

You can use this ddl to test and see the idea of what is going on. It's copy and paste ready, if you want to see a difference in times, make sure you wait before you insert each record.
Create table testing
(
Id int ,
Enter DateTime,
Exitt DateTime,
Eventt int,
GeoCode int
)
insert into testing values (1, GETDATE(),null,6,10)
insert into testing values (1, null,GETDATE(),7,10)
insert into testing values (1, GETDATE(),null,6,11)
insert into testing values (1, null,GETDATE(),7,11)
insert into testing values (2, GETDATE(),null,6,10)
insert into testing values (2, null,GETDATE(),7,10)
create table #temp1
(
Id int, EnterTime datetime, GeoCode int
)
create table #temp2
(
Id int, ExitTime datetime, GeoCode int
)
insert into #temp1
Select Id, MAX(Enter), GeoCode from testing where Eventt = 6 group by Id,GeoCode
insert into #temp2
Select Id, MAX(Exitt),GeoCode from testing where Eventt = 7 group by Id,GeoCode
Select t1.Id, t1.EnterTime,t2.ExitTime, t1.GeoCode, DATEDIFF(ss,t1.EnterTime,t2.ExitTime)
from #temp1 t1
inner join #temp2 t2 on t2.Id = t1.Id
and t1.GeoCode = t2.GeoCode
This is basically pseudo code so your going to need to modify, but everything you need is here.

Im gonna guess that eventcode = 6 means thats the intake time
if so two of your data paris dont make much sense as the exit time is before the intake time,
The Query below only accounts for when amd if eventcode 6 = intake time
and the fact that exit time should be before entertime.
query is based on the output you provided and not the view query.
if doing a select * on your view table gives you that output then replace vw_table with yourviewstablename
There are Nulls in the timedif of sqlfiddle because
there was only one instance of assetid 2
assetid 4 and 6 have exit time that happened before entertimes
SQLFIDDLE
select
v1.iAssetid,
v1.EnterTime,
v2.ExitTime,
datediff(minute, v1.Entertime, v2.Exittime) timedif
from vw_table v1
left join vw_table v2 on
v1.iAssetid= v2.iAssetid
and v1.sCategoryNamea = v2.sCategoryNamea
and v2.eEventcode = 7
and v2.dtUTCDatetime >= v1.dtUTCDatetime
where
v1.eEventcode = 6

You can merge two result sets by adding Row_Number to them and then join on that. Like
SELECT DISTINCT tbl1.col1, tbl2.col2
FROM
(SELECT FirstName AS col1, ROW_NUMBER() OVER (ORDER BY FirstName) Number FROM dbo.UBUser) tbl1
INNER JOIN
(SELECT LastName AS col2, ROW_NUMBER() OVER (ORDER BY LastName) Number FROM dbo.UBUser) tbl2
ON tbl1.Number = tbl2.Number
This way you will be able to have EnterTime and ExitTime in the Same Row with Time Difference in the Column Next to it.

Try this
SELECT iAssetid,
iGeoFenceId,
iGeoFenceName,
sCategoryNamea,
iCategoryid,
sSiteName,
Max(EnterTime) As EnterTime,
Min(ExitTime) As ExitTime,
Datediff(minute, Max(EnterTime), Min(ExitTime)) As Timediff
FROM #vw_Table
GROUP BY iAssetid,
iGeoFenceId,
iGeoFenceName,
sCategoryNamea,
iCategoryid,
sSiteName

Related

How to get rid of the redundant information of a query and be left with only one row that contains all the information

Hello I have the following query in postgres. And I get the following result.
create temporary table "query_table" as
select
"airgoLocator_surgerytimes".iqnum as "surgery",
adminssion_time as "Admissió in",
pre_enter_time as "Preparació in",
quiro_enter_time as "Quiròfan in",
quiro_exit_time as "Quiròfan out",
recu_enter_time as "Recuperació in",
exit_time as "Sortida",
max(case when ("airgoLocator_phase".name='Inici anestèsia') then "airgoLocator_phasehistory".timestamp else NULL end) as Inici_anestesia,
max(case when ("airgoLocator_phase".name='Inici cirurgia') then "airgoLocator_phasehistory".timestamp else NULL end) as Inici_cirurgia,
max(case when ("airgoLocator_phase".name='Fi cirurgia') then "airgoLocator_phasehistory".timestamp else NULL end) as Fi_cirurgia,
max(case when ("airgoLocator_phase".name='Fi anestèsia') then "airgoLocator_phasehistory".timestamp else NULL end) as Fi_anestesia
from
"airgoLocator_surgerytimes"
inner join
"airgoLocator_surgery"
on
"airgoLocator_surgerytimes".iqnum = "airgoLocator_surgery".iqnum
inner join
"airgoLocator_phasehistory"
on
"airgoLocator_surgery".id = "airgoLocator_phasehistory".surgery_id
inner join
"airgoLocator_phase"
on
"airgoLocator_phasehistory".phase_id = "airgoLocator_phase".id
--where "airgoLocator_surgerytimes".iqnum = '0018571064'
group by "airgoLocator_surgerytimes".iqnum, "airgoLocator_surgerytimes".adminssion_time,
"airgoLocator_surgerytimes".pre_enter_time, "airgoLocator_surgerytimes".quiro_enter_time,
"airgoLocator_surgerytimes".quiro_exit_time, "airgoLocator_surgerytimes".recu_enter_time,
"airgoLocator_surgerytimes".exit_time, "airgoLocator_phase".name, "airgoLocator_phasehistory".timestamp,
"airgoLocator_surgerytimes".id, "airgoLocator_phasehistory".phase_id
order by "airgoLocator_surgerytimes".id desc, phase_id asc
;
select * from "query_table" where surgery = '0018571064';
My question is how can I get rid of the null fields and the repeated data so that I am left with a single row with all the information. For example like this:
Due to the way you are adding the rows into your temp table, with null values, we have to filter for the columns that do not have null values. You can get the result you want in two different ways. Using either group by, or using inner join on the same table multiple times. Here is an example of both:
Using group by:
select distinct
t1.surgury,
t1.Admission,
t1.preperation,
t1.Quirofan_in,
t1.Quirofan_out,
t1.recuperacion,
t1.sortida,
max(t1.inici_anestesia) [inici_anestesia],
max(t1.inici_cirurgia)[inici_cirurgia],
max(t1.fi_cirurgia)[fi_cirurgia]
from table2 t1
group by surgury, Admission, preperation, Quirofan_in, Quirofan_out, recuperacion, sortida
Using inner join method:
select distinct
t1.surgury,
t1.Admission,
t1.preperation,
t1.Quirofan_in,
t1.Quirofan_out,
t1.recuperacion,
t1.sortida,
t2.inici_anestesia,
t3.inici_cirurgia,
t4.fi_cirurgia
from table2 t1
inner join table2 t2 on t2.surgury=t1.surgury and t2.inici_anestesia is not null
inner join table2 t3 on t3.surgury=t1.surgury and t3.inici_cirurgia is not null
inner join table2 t4 on t2.surgury=t1.surgury and t4.fi_cirurgia is not null
I can tell you a solution, but you need to know your data and check if this solution does not match with your expected result.
You can remove those columns from group by clause and wrap them with MAX Function in SELECT clause.
But if the values in different rows (except null values) are different you miss them. In this case you may decide to use SUM function. It depends on your business goal.
Because your query is very long; I provide you a simple sample to understand.
Create Table Test (
ID int,
groupColumn1 int,
groupColumn2 int
)
INSERT Test (ID, groupColumn1, groupColumn2)
Values (1, 10, 20),
(1, 10, NULL),
(1, NULL, 20)
select ID, Max(groupColumn1) groupColumn1, MAX(groupColumn2) groupColumn2
From Test
Group by ID

How to join two tables having two common column values and unioning the rest

I'd like to combine Table A and Table B at the link below and end up with Table C. What is the best way to do this in SQL? I've thought about creating a composite key between the tables for LedgerID + Year doing an inner join and then unioning the left and right only data. I'm also curious how to avoid duplicating values across rows like Balance = 50.00 ending up in rows for Tires and Windshield.
Try a full outer join, joining on LedgerID and Year, using coalesce to show Table B's LedgerID/Year when Table A's is NULL:
SELECT
COALESCE(A.LedgerID, B.LedgerID) as LedgerID,
COALESCE(A.Year, B.Year) as Year,
A.Title,
A.Payment,
B.Balance
FROM "Table A" AS A
FULL OUTER JOIN "Table B" AS B ON (A.LedgerID=B.LedgerID AND A.Year=B.Year)
--Please try this Query. Since you have only reference LedgerId and Year, the balance will show 50 for both Tires & Windshield
; with cte_Ledger (LedgerId, [year])
AS
(
Select DISTINCT LedgerId, [year]
From tableA
UNION
Select DISTINCT LedgerId, [year]
From tableB
)
select t.LedgerId
, t.[year]
, t1.Title
, T1.Payments
, t2.Balance
FROM cte_Ledger t
left join tableA t1 on t.LedgerId = t1.LedgerId and t.[year] = t1.[year]
left join tableB t2 on t2.LedgerId = t.LedgerId and t2.[year] = t.[year]
I think so, Above Queries will not help to get expected result.
some misunderstanding is with requirement.
For ledgerid = 22 and Year = 2017, have 2 records in table-A and 1 with Table-B. But in expecting result, Balance 50(Record of table-B) is exists with matched first row of Table-A only. As per above all logic it will be with 2 Records where ledgerid = 22, Year = 2017 and Title with "Tires" & "Windshield".
If required same result as mentioned then need to use recursive CTE or ranking function with order of ID column.
Here is my solution after I loaded the tables, another nested case statement may be need to format out the zero on Ledger 24.
Select
[LedgerID],
[Year],
Case when PayRank = 1 then Title else '' end as Title,
Case when PayRank = 1 then convert(varchar(20),Payments) else '' end as
Payments,
Case when BalRank = 1 then convert(varchar(20),Balance) else '' end as
Balance
from(
SELECT
B.[LedgerID]
,B.[Year]
,Rank()Over(Partition by B.LedgerID,Payments order by
B.LedgerID,B.Year,Title) as PayRank
,isnull([Title],'') as Title
,isnull([Payments],0) as Payments
,Rank()Over(Partition by B.LedgerID,B.Year order by
B.LedgerID,B.Year,Payments) as BalRank
,Balance
FROM [TableB] B
left outer join [TableA] A
on A.LedgerID = B.LedgerID
) Query
order by LedgerID,Year

Compare fields from different rows

First off I am using SQL Server.
I am joining a table on itself like in the example below:
SELECT t.theDate,
s.theDate,
t.bitField,
s.bitField,
t.NAME,
s.NAME
FROM table1 t
INNER JOIN table1 s ON t.NAME = s.NAME
If I take a random row (i.e. X) from the dataset produced.
Can I compare values in any field on row X to values in any field on row X-1 OR row X+1?
Example: I want to compare t.theDate on row 5 to s.theDate on row 4 or s.theDate on row 3.
Sample data looks like:
Desired results:
I want to pull all pairs of rows where the t.bitfield and s.bitfield are opposite and t.theDate and s.theDate are opposite.
From the image the would be row (3 & 4), (5 & 6), (7 & 8) ... etc.
I really appreciate any help!
Can it be done?
Varinant 1: It looks like you would like to use ranking function.
if objcet_id('tempdb..#TmpOrderedTable') is not null drop table #TmpOrderedTable
select *, row_number(order by columnlist, (select 0)) rn
into #TmpOrderedTable
from table1 t
select *
from #TmpOrderedTable t0
inner join #TmpOrderedTable tplus on t0.rn = tplus.rn + 1 -- next one
inner join #TmpOrderedTable tminus on t0.rn = tminus.rn - 1 -- previous one
Varinant 2:
To get scalar values you can use ranking function lag and lead. Or subquery.
Varinant 3:
You can use selfjoin, but you have to specify unique nonarbitary key if you don't want duplicates.
Varinant 4:
You can use apply.
Your question isn't too clear, so i hope it was your goal.
How about this?
WITH ts as (
SELECT t.theDate as theDate1, s.theDate as theDate2,
t.bitField as bitField1, s.bitField as bitField2,
t.NAME -- there is only one name
FROM table1 t INNER JOIN
table1 s
ON t.NAME = s.NAME
)
SELECT ts.*
FROM ts
WHERE EXISTS (SELECT 1
FROM ts ts2
WHERE ts2.name = ts.name AND
ts2.theDate1 = ts.theDate2 AND
ts2.theDate2 = ts.theDate1 AND
ts2.bitField1 = ts.bitField2 AND
ts2.bitField2 = ts.bitField1
);

Finding values that matches max column in SQL server

I have a table that has the max months column like the one below:
Category MaxMonths Title
X 3 Beginner-1
X 6 Intermediate-1
X 12 Avance-1
X 999 Master-1
Y 3 Beginner-2
Y 6 Intermediate-2
Y 12 Avance-2
Y 999 Master-2
I also have another table with a number of months column like the one below:
User #months Category
A 1 X
B 5 X
C 6 y
D 12 y
E 15 X
How can I write a case statement that shows based on the #months in the second table, what the user's title is?
For example, it needs to show user A's title is Beginner-1. I am able to join the tables using the category column but have a hard time matching the #months with maxMonths
This query finds the maxMonths applicable for each user/category:
select t2.[User], t2.category, min(t1.maxMonths) as maxMonths
from table1 t1
inner join table2 t2 on t1.category = t2.category
and t1.maxMonths >= t2.months
group by t2.[User], t2.category
You can then easily get the title:
select r1.[User], r2.Title
from
(select t2.[User], t2.category, min(t1.maxMonths) as maxMonths
from table1 t1
inner join table2 t2 on t1.category = t2.category
and t1.maxMonths >= t2.months
group by t2.[User], t2.category) r1
inner join table1 r2 on r2.category = r1.category and r2.maxMonths = r1.maxMonths;
Here's your sample data in temp tables:
CREATE TABLE #title(
category CHAR(1)
,maxMonths INT
,title VARCHAR(25)
)
GO
CREATE TABLE #months(
[user] CHAR(1)
,months INT
,category CHAR(1)
)
GO
INSERT INTO #title VALUES
('X',3 ,'Beginner-1')
,('X',6 ,'Intermediate-1')
,('X',12 ,'Avance-1')
,('X',999,'Master-1')
,('Y',3 ,'Beginner-2')
,('Y',6 ,'Intermediate-2')
,('Y',12 ,'Avance-2')
,('Y',999,'Master-2')
GO
INSERT INTO #months VALUES
('A',1 ,'X')
,('B',5 ,'X')
,('C',6 ,'y')
,('D',12,'y')
,('E',15,'X')
GO
You can use the following query to get what you're looking for.
SELECT
#months.[user]
,#months.months
,#months.category
,#title.title
FROM #title
INNER JOIN #months ON #title.category = #months.category
WHERE #title.maxmonths = (
SELECT
MIN(maxmonths)
FROM #title
WHERE maxmonths >= #months.months
)
Explanation: The nested query will limit the results to just the title with the maxMonths just above the actual months
Notes: I wasn't sure if you wanted a match on the months = maxMonth to go up a level or stay down. I assumed the latter, though you can change that by changing the >= in the nested query to >
Also, you used USER as a column name. You should refrain from using that in production environments, as it is a reserved keyword.
Use the following query
Select
T1.User,
T2.Title
from table1 T1
inner join table2 T2 on t1.category = t2.category
Where t2.MaxMonthes = (
Select
min(MaxMonthes)
from table1 T3
Where T3.maxmonthes >= T2.[#months]
and T2.category = T3.category
)

Multiple MAX values select using inner join

I have query that work for me only when values in the StakeValue don't repeat.
Basically, I need to select maximum values from SI_STAKES table with their relations from two other tables grouped by internal type.
SELECT a.StakeValue, b.[StakeName], c.[ProviderName]
FROM SI_STAKES AS a
INNER JOIN SI_STAKESTYPES AS b ON a.[StakeTypeID] = b.[ID]
INNER JOIN SI_PROVIDERS AS c ON a.[ProviderID] = c.[ID] WHERE a.[EventID]=6
AND a.[StakeGroupTypeID]=1
AND a.StakeValue IN
(SELECT MAX(d.StakeValue) FROM SI_STAKES AS d
WHERE d.[EventID]=a.[EventID] AND d.[StakeGroupTypeID]=a.[StakeGroupTypeID]
GROUP BY d.[StakeTypeID])
ORDER BY b.[StakeName], a.[StakeValue] DESC
Results for example must be:
[ID] [MaxValue] [StakeTypeID] [ProviderName]
1 1,5 6 provider1
2 3,75 7 provider2
3 7,6 8 provider3
Thank you for your help
There are two problems to solve here.
1) Finding the max values per type. This will get the Max value per StakeType and make sure that we do the exercise only for the wanted events and group type.
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
2) Then we need to get only one return back for that value since it may be present more then once.
Using the Max Value, we must find a unique row for each I usually do this by getting the Max ID is has the added advantage of getting me the most recent entry.
SELECT MAX(SMaxID.ID) AS ID
FROM SI_STAKES AS SMaxID
INNER JOIN (
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
) AS SMaxVal ON SMaxID.StakeTypeID = SMaxVal.StakeTypeID
AND SMaxID.StakeValue = SMaxVal.MaxStakeValue
AND SMaxID.EventID = SMaxVal.EventID
AND SMaxID.StakeGroupTypeID = SMaxVal.StakeGroupTypeID
3) Now that we have the ID's of the rows that we want, we can just get that information.
SELECT Stakes.ID, Stakes.StakeValue, SType.StakeName, SProv.ProviderName
FROM SI_STAKES AS Stakes
INNER JOIN SI_STAKESTYPES AS SType ON Stake.[StakeTypeID] = SType.[ID]
INNER JOIN SI_PROVIDERS AS SProv ON Stake.[ProviderID] = SProv.[ID]
WHERE Stake.ID IN (
SELECT MAX(SMaxID.ID) AS ID
FROM SI_STAKES AS SMaxID
INNER JOIN (
SELECT StakeGroupTypeID, EventID, StakeTypeID, MAX(StakeValue) AS MaxStakeValue
FROM SI_STAKES
WHERE Stake.[EventID]=6
AND Stake.[StakeGroupTypeID]=1
GROUP BY StakeGroupTypeID, EventID, StakeTypeID
) AS SMaxVal ON SMaxID.StakeTypeID = SMaxVal.StakeTypeID
AND SMaxID.StakeValue = SMaxVal.MaxStakeValue
AND SMaxID.EventID = SMaxVal.EventID
AND SMaxID.StakeGroupTypeID = SMaxVal.StakeGroupTypeID
)
You can use the over clause since you're using T-SQL (hopefully 2005+):
select distinct
a.stakevalue,
max(a.stakevalue) over (partition by a.staketypeid) as maxvalue,
b.staketypeid,
c.providername
from
si_stakes a
inner join si_stakestypes b on
a.staketypeid = b.id
inner join si_providers c on
a.providerid = c.id
where
a.eventid = 6
and a.stakegrouptypeid = 1
Essentially, this will find the max a.stakevalue for each a.staketypeid. Using a distinct will return one and only one row. Now, if you wanted to include the min a.id along with it, you could use row_number to accomplish this:
select
s.id,
s.maxvalue,
s.staketypeid,
s.providername
from (
select
row_number() over (order by a.stakevalue desc
partition by a.staketypeid) as rownum,
a.id,
a.stakevalue as maxvalue,
b.staketypeid,
c.providername
from
si_stakes a
inner join si_stakestypes b on
a.staketypeid = b.id
inner join si_providers c on
a.providerid = c.id
where
a.eventid = 6
and a.stakegrouptypeid = 1
) s
where
s.rownum = 1