SQL Min value not being selected - sql-server

I have a select statement with a joining table, and I am attempting to select the first row of the joined table.
For example, dbo.Projects has many dbo.Buffers.
My query is:
SELECT PM.PROJECTID, PM.PROJECTNAME, BU.PERCENTPENWREALIGNED
FROM dbo.PROJECTMGRVIEW AS PM
JOIN dbo.S2M_BUFFER AS BU ON BU.PROJECTID = ( SELECT DISTINCT MIN(TASKUNIQUEID) FROM dbo.S2M_BUFFER WHERE PROJECTID = PM.PROJECTID )
WHERE PM.PROJECT_TYPE = 8 AND PM.CATEGORY = 'Engineering' ANd PM.PROJECTID = 244;
My result set is many rows:
PROJECTID | PROJECTNAME | PERCENTPENWREALIGNED
244 | PROJECT A | 100
244 | PROJECT A | 0
244 | PROJECT A | 0
244 | PROJECT A | 0
244 | PROJECT A | 0
244 | PROJECT A | 0
244 | PROJECT A | 0
Obviously in this case, I simply need the first row.

Your join doesn't really make any sense, you can't do it using a subquery, but my guess is you want something like this:
SELECT PM.PROJECTID, PM.PROJECTNAME, BU.PERCENTPENWREALIGNED
FROM dbo.PROJECTMGRVIEW AS PM
CROSS APPLY (
select top 1 PERCENTPENWREALIGNED
from dbo.S2M_BUFFER BU
where BU.PROJECTID = PM.PROJECTID
order by TASKUNIQUEID ASC
) BU
WHERE PM.PROJECT_TYPE = 8 AND PM.CATEGORY = 'Engineering' AND PM.PROJECTID = 244;
This will join the row with smallest TASKUNIQUEID in S2M_BUFFER with the PROJECTMGRVIEW

I agree your Join statement seems flawed due a circular reference between the PROJECTID and the TASKUNIQUEID.
I think this might be more what you were trying to do:
SELECT PM.PROJECTID, PM.PROJECTNAME, BU.PERCENTPENWREALIGNED
FROM dbo.PROJECTMGRVIEW AS PM
JOIN dbo.S2M_BUFFER AS BU ON BU.TASKUNIQUEID =
( SELECT DISTINCT MIN(TASKUNIQUEID) FROM dbo.S2M_BUFFER WHERE PROJECTID = PM.PROJECTID )
WHERE PM.PROJECT_TYPE = 8 AND PM.CATEGORY = 'Engineering' ANd PM.PROJECTID = 244;

Related

How to repeat values in case of null values on left join

I have a table with a calendar, and a table with rates. In the table with the rates, there are no values existing for days in the weekend. I'm trying to join the two, in order to have a table where there is a rate for all days, and I need the rates in the weekend to be the latest available rate. Instad of it showing NULL values, as it would when you make a left join and the record doesn't exist, it should just take the latest available, repeating the previous value.
I have the below code, which works, but it takes 2 min to do on 7,397 rows, which is way too long.
Does anyone know a faster way to get the same results?
SELECT
c.CalendarID,
MAX(r.RateID)
FROM Dim_Calendar c
LEFT JOIN Dim_Rates r ON r.RateDate <= c.CalendarID
What I get without <= and just an = is the following
CalendarID | RateID
20131001 | 2
20131002 | 3
20131003 | 4
20131004 | 5
20131005 | NULL
20131006 | NULL
20131007 | 6
And this is the desired table:
CalendarID | RateID
20131001 | 2
20131002 | 3
20131003 | 4
20131004 | 5
20131005 | 5
20131006 | 5
20131007 | 6
You can use LAG() window function:
SELECT c.CalendarID,
COALESCE(
r.RateID,
LAG(r.RateID, 1) OVER (ORDER BY c.CalendarID),
LAG(r.RateID, 2) OVER (ORDER BY c.CalendarID)
) RateID
FROM Dim_Calendar c LEFT JOIN Dim_Rates r
ON r.RateDate = c.CalendarID
ORDER BY c.CalendarID
See the demo.
Results:
> CalendarID | RateID
> ---------: | :-----
> 20131001 | 2
> 20131002 | 3
> 20131003 | 4
> 20131004 | 5
> 20131005 | 5
> 20131006 | 5
> 20131007 | 6
You could use a correlated subquery to fill the gaps:
SELECT
c.CalendarID,
(SELECT TOP 1 r.RateID FROM Dim_Rates r
WHERE r.RateDate <= c.CalendarID AND r.RateID IS NOT NULL
ORDER BY r.RateDate DESC) AS RateID
FROM Dim_Calendar c
ORDER BY c.CalendarID;
This query can be improved by using the following index:
CREATE INDEX idx ON Dim_Rates (RateDate, RateID);
As pointed out, you need to check for proper and covering indexing. It appears you are running a against a DW DB and if that is the case then you can replace the CTE with indexed temp tables if the esitmated row count approximation is way off in the query plan.
;WITH NormalizedData AS
(
SELECT
RateID,CalendarID,
VirtualGroupID = SUM(LastRecordBeforeGap) OVER (ORDER BY CalendarID ROWS UNBOUNDED PRECEDING)
FROM
(
SELECT RateID,CalendarID,
LastRecordBeforeGap = CASE WHEN LEAD(RateID) OVER(ORDER BY CalendarID) IS NULL AND RateID IS NOT NULL THEN 1 ELSE 0 END
FROM
Dim_Calendar c
LEFT JOIN Dim_Rates r ON r.RateDate = c.CalendarID
)AS x
)
SELECT
RateID = ISNULL(RateID, SUM(RateID) OVER(PARTITION BY VirtualGroupID)),
CalendarID
FROM
NormalizedData

Select only table that don't meet specific condition

I have 2 table and i tried to get table 1 which contains Job_ID which has not finish the progress in table 2.
Table 1. [Job]
Job_ID (key) |GroupID
1410 | A
2309 | B
3456 | C
Table 2. [Progress]
Job_ID |Percent
1410 | 10
1410 | 50
1410 | 60
2309 | 50
2309 | 100
3456 | 100
Expected:
Job_ID (key) |GroupID
1410 | A
My SQL Query is:
SELECT FROM Job,(SELECT DISTINCT * FROM Progress
WHERE Percent<100) AS PGR
WHERE Job.Job_ID = PGR.Job_ID
But i still get this
Job_ID (key) |GroupID
1410 | A
2309 | B
My logic was getting only Job_ID which has Percent < 100 but i failed to find specific way in SQL in stackoverflow. Please help me in this case.
You can take help from below Query.
SELECT *
FROM table1 a
WHERE NOT EXISTS (SELECT 1 FROM table2 b WHERE a.job_id = b.job_id and percentage = 100)
Use LEFT JOIN and NULL check?
SELECT T1.JobID, T2.GroupID
FROM Table1 T1
LEFT JOIN T2 ON T1.JobID = T2.JobId
AND T2.Percent = 100
WHERE T2.JobID IS NULL;
use not exists
select
a.* from job a
where not exists (select 1 from progres b
where a.job_id =b.job_id and b.Percent=100
)
Try this
select
job.*
from
job
where
not exists (select 1 from progres where progres.job_id =job.job_id)

Sum variable amount of intervals together

we just changed our telephony system and every agents are now being logged through 15 minute intervals and we need 1 line per event
table event:
empid | code | timestamp | duration
5111 | 5 | 09:45:00 | 45
5222 | 2 | 09:58:00 | 120
5111 | 5 | 10:00:00 | 900
5111 | 5 | 10:15:00 | 900
5111 | 5 | 10:15:30 | 30
5222 | 5 | 11:00:00 | 8
5222 | 5 | 11:00:05 | 5
timestamp is writen after the fact, so a timestamp at 9:45:00 with a duration of 45 was from 9:44:15 and since the interval stopped at 9:45, it was written at that time, but i need 9:44:15 save
result should give me
empid | code | timestamp | duration
5111 | 5 | 09:44:15 | 1875
5222 | 2 | 09:56:00 | 120
5222 | 5 | 10:59:52 | 13
The problem is the phones are locked with a 2 hours max delay, and as you can see with my employee # 5222 he spent 13 seconds on two lines... i could join the same table 10 times. 1 to avoid when there is the same code where the end time of the previous line = the starttime of the new line
this is on MSSQL 2008
Select e.empid
,e.code
,convert(time(0),DATEADD(ss,- e.Duration, e.timestamp))
,e.duration + isnull(e1.duration,0) + isnull(e2.duration,0)
from [event] e
left join [event] e0 on
convert(TIME(0),DATEADD(ss,- e.Duration, e.timestamp)) = e0.timestamp
and
e.empid = e0.empid
and
e.code = e0.code
left join [event] e1 on
convert(TIME(0),DATEADD(ss,- e1.Duration, e1.timestamp)) = e.timestamp
and
e.empid = e1.empid
and
e.code = e1.code
left join [event] e2 on
convert(TIME(0),DATEADD(ss,- e2.Duration, e2.timestamp)) = e1.timestamp
and
e2.empid = e1.empid
and
e2.code = e1.code
--etc......
where isnull(e0.duration,'-10') = '-10'
This works but far from optimal...
i would rather use an aggregate function but i dont know how to write it as there is no comon key other than last timestamps match with new - duration with this table!
it is important to know that agent 5111 could go again on code 5 on the same day, and i would need 2 lines for this one.... if not it would have been too easy!
thank you in advance!
Try this. I have commented in the code, but the basic algorithm
find rowswhich are continuations i.e. there exists a row which matches once
you subtract the duration
find the "originals" i.e. the start of each call by subtracting the continuations
for each original, find the next original so we can determine a range of times to look for continuations
join it all together and add the total duration from continuations appropriate to each original
Hope this helps, it was an interesting challenge!
declare #data table
(
empid int,
code int,
[timestamp] time,
duration int
);
insert into #data values(5111,5,'09:45',45),
(5222,2,'09:58',120),
(5111,5,'10:00',900),
(5111,5,'10:15',900),
(5111,5,'10:15:30',30),
(5222,5,'11:00',8),
(5222,5,'11:00:05',5),
-- added these rows to include the situation you describe where 5111 goes again on code 5:
(5111,5,'13:00',45),
(5111,5,'13:15',900),
(5111,5,'13:15:25',25);
-- find where a row is a continuation
with continuations as (
select a.empid, a.code, a.[timestamp] , a.duration
from #data a
inner join #data b on a.empid = b.empid
and a.code = b.code
where dateadd(ss, -a.duration, a.[timestamp]) = b.[timestamp]
),
-- find the "original" rows as the complement of continuations
originals as
(
select d.empid, d.code, d.[timestamp], d.duration
from #data d
left outer join continuations c on d.empid = c.empid and d.code = c.code and d.timestamp = c.timestamp
where c.empid is null
),
-- to hand the situation where we have more than one call for same agent and code,
-- find the next timestamp for each empid/code
nextcall as (
select a.*, a2.[timestamp] nex
from originals a
outer apply (
select top 1 [timestamp]
from originals a2
where a2.[timestamp] > a.[timestamp]
and a.empid = a2.empid
and a.code = a2.code
order by a2.[timestamp] desc
) a2
)
select o.empid,
o.code,
dateadd(ss, -o.duration, o.timestamp) as [timestamp],
o.duration + isnull(sum(c.duration),0) as duration
from originals o
left outer join nextcall n on o.empid = n.empid and o.code = n.code and o.[timestamp] = n.[timestamp]
left outer join continuations c on o.empid = c.empid
and o.code = c.code
-- filter the continuations on the range of times based on finding the next one
and c.[timestamp] > o.[timestamp]
and (n.nex is null or c.[timestamp] < n.nex)
group by o.empid,
o.code,
o.duration,
o.[timestamp]

On Date base record return

I have table structure like this
vehid TimeFirst TimeLast Inside
1 26/06/2014 null 0
2 26/06/2014 26/06/2014 1
2 26/06/2014 null 0
3 26/06/2014 26/06/2014 1
I want to return only those record on the base of veh enter and left on same day and only pick left record of those vehls.Inside 0 mean veh left and 1 mean enter.expect output below
vehid TimeFirst TimeLast Inside
2 26/06/2014 null 0
Here, you might need to exclude the time from the Datetime :
select * from tableName a
where
convert(date, timeFirst) in ( select convert(date, timeLast) from tableName where id = a.id)
and inside = 0
Try something like this:
SELECT Ent.VehID, Ent.TimeFirst, Exit.TimeLast, Exit.Inside
FROM myTable Ent
JOIN myTable Exit
ON Ent.TimeFirst = Exit.TimeFirst
AND Ent.VehID = Exit.VehID
AND Ent.Inside = 1 AND Exit.Inside = 0
If there are multiple entrance and exits for same day for same vehicle you will get duplicate records and you would need to use Distinct.
Try this
SELECT S.* FROM Table1 T JOIN Table1 S ON
T.TimeFirst = T.TimeLast AND
T.Inside = 1 AND
T.TimeLast = S.TimeFirst
WHERE S.Inside = 0 AND S.Vehid = T.Vehid
FIDDLE DEMO
Output would be:
+---------+---------------+---------------+-------------+
| VEHID | TIMEFIRST | TIMELAST | INSIDE |
+---------+---------------+---------------+-------------+
| 2 | 2014-06-26 | (null) | 0 |
+---------+---------------+---------------+-------------+
You can use this
SELECT P2.*
FROM Parking P1 INNER JOIN
Parking P2 ON P2.VehID = P1.VehID AND
P2.TimeFirst = P1.TimeLast
WHERE P1.Inside = 1 AND P2.Inside = 0

Get value with MAX(date) from two table

I have two tables.
MainTable:
MainID | LastValue | LastReadingDate
1 | 234 | 01.01.2012
2 | 534 | 03.02.2012
Readings:
MainID | ValueRead | ReadingDate
1 | 123 | 03.02.2012
1 | 488 | 04.03.2012
2 | 324 | 03.02.2012
2 | 683 | 05.04.2012
I want to get
SELECT MainTable.MainID, MainTable.LastValue, MainTable.LastReadingDate, (SELECT ValueRead, MAX(ReadingDate)
FROM Readings
WHERE Readings.MainID=MainTable.MainID ORDER BY ValueRead)
In other words, I want to get the current LastValue and LastReadingDate from MainTable along side the ValueRead with the most recent ReadingDate from Readings.
Here is a query you could use. It'll show all MainTable entries, including those that doesn't have a "Reading" entry yet. Change the LEFT JOIN to an INNER JOIN if you don't want it like that.
WITH LastReads AS (
SELECT ROW_NUMBER() OVER (PARTITION BY MainID ORDER BY ReadingDate DESC) AS ReadingNumber,
MainID,
ValueRead,
ReadingDate
FROM Readings
)
SELECT M.MainID, M.LastValue, M.LastReadingDate, R.ValueRead, R.ReadingDate
FROM MainTable M
LEFT OUTER JOIN LastReads R
ON M.MainID = R.MainID
AND R.ReadingNumber = 1 -- Last reading, use 2 or 3 to get the 2nd newest, 3rd newest, etc.
SQLFiddle-link: http://sqlfiddle.com/#!3/16c68/3
Another link with N number of readings per mainid: http://sqlfiddle.com/#!3/16c68/4
Not tried this myself, but here goes. Please try
select max(r.readingdate), max(t.lastvalue), max(t.lastreadingdate)
from readings r inner join
( select MainID, LastValue, LastReadingDate
from MainTable m
where LastReadingDate =
(select max(minner.LastReadingDate)
from MainTable minner
where minner.MainID = m.MainID
)
) t
on (r.mainid = t.mainid)
try this:
select M.LastValue, M.LastReadingDate,
(select top 1 ValueRead from Readings where MainID=M.MainID order by ReadingDate desc)
from MainTable M

Resources