SQL GROUP BY or JOIN - sql

I am using SQL 2008 and just cannot get this query to work. I have a table with flight price details in it. There are many rows for each day for each route and I want to return the lowest price per day for the route and also the DateAdded (the datetime that the row was added). I am almost there as I seem to have managed to get the lowest price per day returned but I can't seem to get the correct dateadded to be returned. The code below seems to work to return the lowest price per day but I think I need some sort of join to also return the DateAdded?
ALTER PROCEDURE [dbo].[FlightInfoLowestPricePerDay]
(
#AirportFrom varchar(5),
#AirportTo varchar(5)
)
AS
select DATEADD(dd, 0, DATEDIFF(dd, 0, TimeDeparture)) as FlightDate, MIN(Price) as MinPrice
from FlightInfo
where AirportFrom = #AirportFrom and AirportTo = #AirportTo
AND TimeDeparture > DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
Group By DATEADD(dd, 0, DATEDIFF(dd, 0, TimeDeparture))
Order by FlightDate ASC
I have tried things such as the code below but I do not fully understand joins yet so have been struggling for quite a while although I'm sure I'm missing something very simple!
ALTER PROCEDURE [dbo].[FlightInfoLowestPricePerDay]
(
#AirportFrom varchar(5),
#AirportTo varchar(5)
)
AS
select DATEADD(dd, 0, DATEDIFF(dd, 0, fi1.TimeDeparture)) as FlightDate, MIN(fi1.Price) as MinPrice, fi2.DateAdded
from FlightInfo
fi1 join FlightInfo fi2 on fi1.Price = fi2.Price
where fi1.AirportFrom = #AirportFrom and fi1.AirportTo = #AirportTo
AND fi1.TimeDeparture > DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
Group By DATEADD(dd, 0, DATEDIFF(dd, 0, fi1.TimeDeparture))
Order by FlightDate ASC

Add fi2.DateAdded in Group By
ALTER PROCEDURE [dbo].[FlightInfoLowestPricePerDay]
(
#AirportFrom varchar(5),
#AirportTo varchar(5)
)
AS
select DATEADD(dd, 0, DATEDIFF(dd, 0, fi1.TimeDeparture)) as FlightDate, MIN(fi1.Price) as MinPrice, Cast(Convert(varchar(20),fi2.DateAdded,101) as datetime) DateAdded
from FlightInfo
fi1 join FlightInfo fi2 on fi1.Price = fi2.Price
where fi1.AirportFrom = #AirportFrom and fi1.AirportTo = #AirportTo
AND fi1.TimeDeparture > DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
Group By DATEADD(dd, 0, DATEDIFF(dd, 0, fi1.TimeDeparture)),Cast(Convert(varchar(20),fi2.DateAdded,101) as datetime)
Order by FlightDate ASC

OK I decided to simplify it a bit as I've wasted too much time on this already so now I am passing a specific date into the query and running the query multiple times to get the results I need:
ALTER PROCEDURE [dbo].[FlightInfoLowestPriceDateAddedForSpecificDate]
(
#AirportFrom varchar(5),
#AirportTo varchar(5),
#Date datetime
)
AS
select top 1 *
from FlightInfo
where AirportFrom = #AirportFrom and AirportTo = #AirportTo
AND DATEADD(dd, 0, DATEDIFF(dd, 0, TimeDeparture)) = DATEADD(dd, 0, DATEDIFF(dd, 0, #Date))
Order by Price asc

You want to use window function for this. One way is with row_number():
select flightDate, price
from (select DATEADD(dd, 0, DATEDIFF(dd, 0, TimeDeparture)) as FlightDate, fi.*,
ROW_NUMBER() over (partition by airportFrom, airportTo, DATEADD(dd, 0, DATEDIFF(dd, 0, TimeDeparture)) order by price) as seqnum
from FlightInfo fi
where AirportFrom = #AirportFrom and AirportTo = #AirportTo and
TimeDeparture > DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
) fi
where seqnum = 1
If you are using more recent versions of SQL Server, then you can take advantage of the date data type:
select flightDate, price
from (select cast(TimeDeparture as date) as FlightDate, fi.*,
ROW_NUMBER() over (partition by airportFrom, airportTo, cast(TimeDeparture as date) order by price) as seqnum
from FlightInfo fi
where AirportFrom = #AirportFrom and AirportTo = #AirportTo and
TimeDeparture > cast(getdate() as date)
) fi
where seqnum = 1

You can two sub queries (one that gets the small prices per day and the second one the DateAdded)
SELECT f1.FlightDate, f1.MinPrice, f2.DateAdded From
(
SELECT
DATEADD(dd, 0, DATEDIFF(dd, 0, fi1.TimeDeparture)) as FlightDate,
MIN(fi1.Price) as MinPrice
FROM FlightInfo fi1
WHERE fi1.AirportFrom = #AirportFrom and fi1.AirportTo = #AirportTo
AND fi1.TimeDeparture > DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
Group By
DATEADD(dd, 0, DATEDIFF(dd, 0, fi1.TimeDeparture))
) f1
INNER JOIN
(
SELECT DISTINCT
DATEADD(dd, 0, DATEDIFF(dd, 0, fi2.DateAdded)) AS DateAdded,
Price FROM FlightInfo fi2
WHERE fi2.AirportFrom = #AirportFrom and fi2.AirportTo = #AirportTo
AND fi2.TimeDeparture > DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()))
) f2 ON f2.Price = f1.MinPrice

Related

How to get value 0 if the result of sql select except statement return no records?

I have a problem with the SQL Except statement, in my case, I select two columns and one column can have values and the others cannot have because the count of the first SQL statement equals the count of the second SQL after the except statement.
Result that I got :
GroupId absences
1 3
Expected result
GroupId absences
1 3
2 0
How can I do in this case?
select
groupId,
(case when groupId = null then 0 else COUNT(*) end) as absence
from (
select
groupId,
COUNT(*) as absences1
from (
select distinct
MONTh,
DAY,
e.groupId
from employee_c c,
holiday hl,
employee e,
groups,
Get_Calendar_Date(
DATEADD(MONTH, DATEDIFF(MONTH, 0, CAST(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) AS datetime)) + #Month - 1, 0),
DATEADD(MONTH, DATEDIFF(MONTH, -1, CAST(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) AS datetime)) + #Month-1, -1)
)
where DATENAME(DD, Weekday) IN (
select WorkingdayId+1
from timetable s,
groups ds,
grouptime de
where dd.groupId = ds.groupId
and dd.groupId = de.groupId
and s.timetableId = de.timetableId
and de.groupId = ds.groupdId)
and DATEPART(MM,hl.startDate) = #Month
and c.isActive=1
except
(select Month,
Day,
d.departmentId
from department d,
Get_Calendar_Date(
DATEADD(MONTH, DATEDIFF(MONTH, 0, CAST(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) AS datetime)) + #Month - 1, 0),
DATEADD(MONTH, DATEDIFF(MONTH, -1, CAST(DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), 0) AS datetime)) + #Month - 1, -1)
),
holiday hle,
employee_c c,
employee e
where datepart(MM,hle.startDate) = #Month
and cast(Date as date) between hle.startDate and hle.endDate
and c.isActive=1
and d.groupId =e.groupId
and c.employeeId=e.employeeId
and c.isActive=1
)
) sc
group by Month,Day,groupId
) s
group by groupId

Show Dates that have no values for selected column

I have the following query which counts the number of items created on a particular date in the last 10 days
SELECT
CONVERT (DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) AS 'Logged Date',
Count (*) AS 'Total'
FROM
MTV_System$WorkItem$Incident
WHERE
CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688 >= DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)
GROUP BY
CONVERT(DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688)
How do I get this to show the dates which have no values present (i.e. get every date value for the last 10 days, return the count if there is data or 0 if none). Using SQL Server 2012.
You can write a recursive cte to get the date for the last 10 days into a table as follows:
WITH TableA (StartDate) AS (SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)),
q as (
SELECT StartDate
, Number = 0
FROM TableA
UNION ALL
SELECT DATEADD(d,1,StartDate)
, Number = Number + 1
FROM q
WHERE 10 > Number )
Then join q with your original query, to get a row for every date.
select q.StartDate, yourtable.Total from q
left join (
SELECT
CONVERT (DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) AS 'Logged Date',
Count (*) AS 'Total'
FROM
MTV_System$WorkItem$Incident
WHERE
CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688 >= DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)
GROUP BY
CONVERT(DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688)
) as yourtable on [Logged Date] = q.StartDate
Similar to BeanFrog's answer but a little shorter
-- sample data for testing
declare #MTV_System$WorkItemIncident table (
[CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688] DATE,
[Total] INT
);
INSERT INTO #MTV_System$WorkItemIncident VALUES ('2015-11-23', 23);
INSERT INTO #MTV_System$WorkItemIncident VALUES ('2015-11-21', 21);
INSERT INTO #MTV_System$WorkItemIncident VALUES ('2015-11-30', 30);
-- now the query
WITH TableA (LoggedDate) AS (
SELECT TOP 10 CONVERT (DATE, DATEADD(DAY, number * -1, GETDATE())) AS 'LoggedDate'
FROM master.dbo.spt_values
WHERE name IS NULL
)
SELECT TableA.[LoggedDate],
SUM(ISNULL(Data.Total, 0)) AS 'LoggedCount'
FROM TableA
LEFT JOIN #MTV_System$WorkItemIncident AS Data ON CONVERT (DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) = TableA.[LoggedDate]
GROUP BY TableA.[LoggedDate]
Not that there is anything wrong with BeanFrog's answer, but if you don't want to use a recursive cte you could do this:
CREATE TABLE MTV_System$WorkItem$Incident (id int PRIMARY KEY, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688 datetime)
INSERT INTO MTV_System$WorkItem$Incident VALUES (1, '20151201')
INSERT INTO MTV_System$WorkItem$Incident VALUES (2, '20151126')
INSERT INTO MTV_System$WorkItem$Incident VALUES (3, '20151127')
INSERT INTO MTV_System$WorkItem$Incident VALUES (4, '20151127')
SELECT
ReportDate AS 'Logged Date',
Count (CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688) AS 'Total'
FROM (
SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 0, 0) AS ReportDate
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 1, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 2, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 3, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 4, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 5, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 6, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 7, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 8, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 9, 0)
UNION SELECT DATEADD(DAY, DATEDIFF(DAY, 0, Getdate()) - 10, 0)
) AS Dates
LEFT JOIN MTV_System$WorkItem$Incident ON ReportDate = CONVERT(DATE, CreatedDate_6258638D_B885_AB3C_E316_D00782B8F688)
GROUP BY ReportDate
BeanFrog's answer has the advantage of being able to easily change the number of days though.

Sql Query: not able to add a column from another table

I am using this query currently which is running correctly:
SELECT
t.TestId,
t.Days,
t.UserId_Fk
, [Date] = Convert(date,DATEADD(dd, 0, DATEDIFF(dd, 0, t.CheckIn)))
, CheckIn = CONVERT(CHAR(5), t.CheckIn, 108)
, CheckOut = CONVERT(CHAR(5), t.CheckOut, 108)
, [Hours] = CAST(DATEDIFF(MINUTE, t.CheckIn, t.CheckOut) / 60. AS DECIMAL(10,2))
FROM (
SELECT
TestId=t.TestId,
Days=t.Days,
t.UserId_Fk
, CheckIn = t.CheckInTime
, CheckOut = r.CheckInTime
, RowNum = ROW_NUMBER() OVER (PARTITION BY t.UserId_Fk, r.CheckInTime ORDER BY 1/0)
FROM UserTime t
OUTER APPLY (
SELECT TOP 1 *
FROM UserTime t2
WHERE
t2.CheckInTime > t.CheckInTime
AND DATEADD(dd, 0, DATEDIFF(dd, 0, t.CheckInTime)) = DATEADD(dd, 0, DATEDIFF(dd, 0, t2.CheckInTime))
AND t2.LoginStatus = 'O'
ORDER BY t2.CheckInTime
) r
WHERE t.LoginStatus = 'I'
) t
WHERE t.RowNum = 1
The result is :
TestId Days UserId_Fk Date CheckIn CheckOut Hours
45 Tuesday 3 2014-05-13 11:49 11:49 0.00
I want to add another field(column) in this result Named FullName which is from another
table Users
I tried various things but was unsuccessful.
Table relations are:
Table Test
TestId int(pk)
UserId_Fk int
Days nvarchar(50)
CheckInTime datetime
LoginStatus char(1)
Table Users
UserId int(Pk)
FullName varchar(50)
First join Test & Users table and then try to select the full name of the user. You can join through userid_fk. After joining you can apply top 1 selection & other formating over that joined table.
Something like below:
SELECT
t.TestId,
t.Days,
t.UserId_Fk
, [Date] = Convert(date,DATEADD(dd, 0, DATEDIFF(dd, 0, t.CheckIn)))
, CheckIn = CONVERT(CHAR(5), t.CheckIn, 108)
, CheckOut = CONVERT(CHAR(5), t.CheckOut, 108)
, [Hours] = CAST(DATEDIFF(MINUTE, t.CheckIn, t.CheckOut) / 60. AS DECIMAL(10,2))
FROM
(SELECT *
FROM test
JOIN users
ON test.userid_fk=users.userid) t
OUTER APPLY (
SELECT TOP 1 *
FROM UserTime t2
WHERE
t2.CheckInTime > t.CheckInTime
AND DATEADD(dd, 0, DATEDIFF(dd, 0, t.CheckInTime)) = DATEADD(dd, 0, DATEDIFF(dd, 0, t2.CheckInTime))
AND t2.LoginStatus = 'O'
ORDER BY t2.CheckInTime
) r
WHERE t.LoginStatus = 'I'
) t
WHERE t.RowNum = 1
Please try few times and fix the query if there is any typo.
SELECT
t.TestId,
t.Days,
t.FullName
, [Date] = Convert(date,DATEADD(dd, 0, DATEDIFF(dd, 0, t.CheckIn)))
, CheckIn = CONVERT(CHAR(5), t.CheckIn, 108)
, CheckOut = CONVERT(CHAR(5), t.CheckOut, 108)
, [Hours] = CAST(DATEDIFF(MINUTE, t.CheckIn, t.CheckOut) / 60. AS DECIMAL(10,2))
FROM (
SELECT
FullName=Users.FullName,
TestId=t.TestId,
Days=t.Days,
t.UserId_Fk
, CheckIn = t.CheckInTime
, CheckOut = r.CheckInTime
, RowNum = ROW_NUMBER() OVER (PARTITION BY t.UserId_Fk, r.CheckInTime ORDER BY 1/0)
FROM UserTime t
Inner join Users
on t.UserId_Fk=Users.UserId
OUTER APPLY (
SELECT TOP 1 *
FROM UserTime t2
WHERE
t2.CheckInTime > t.CheckInTime
AND DATEADD(dd, 0, DATEDIFF(dd, 0, t.CheckInTime)) = DATEADD(dd, 0, DATEDIFF(dd, 0, t2.CheckInTime))
AND t2.LoginStatus = 'O'
ORDER BY t2.CheckInTime
) r
WHERE t.LoginStatus = 'I'
) t
WHERE t.RowNum = 1

update SQL statement and using if exists

I am trying to select a value from one table and insert it into a specfic section of another table. I have the following sql (mssql 2008)
if exists (select datesubmitted from JADHist where datesubmitted = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()-1)))
Begin
Select JadOPNTotal from JADhist where dateSubmitted = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()-1))
end
else
begin
Select jadopntotal from JADhist where dateSubmitted = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()-2))
end
This always retrieves one value with datatype real. I have another table which is 2 columns, one is the date with the hour next to it and there is an entry for the next 2 weeks for every hour. The format is 2013-03-26 01:00:00.000 and so on. I want to insert the value I get from the code above into the 2nd table for one 24 hour period (24 entries). THe 24 period will be today from 6am till tomorrow at 6am.
I also have the following code with a test value of 10 in it just to prove the code updates opndata correctly.
UPDATE [GasNominations].[dbo].[OPNData]
SET
[Value] = '10'
WHERE NomSubmittedDate between DATEADD(HH, 6, DATEDIFF(dd, 0, GETDATE()))
and DATEADD(HH, 5, DATEDIFF(dd, 0, GETDATE()+1))
GO
I want to replace the '10' value with the if exists statement but I cant get the syntax correct.
try this
declare #total varchar(10)
if exists (select datesubmitted from JADHist
where datesubmitted = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()-1)))
Begin
Select #total = JadOPNTotal from JADhist
where dateSubmitted = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()-1))
end
else
begin
Select #total = jadopntotal from JADhist
where dateSubmitted = DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE()-2))
end
UPDATE [GasNominations].[dbo].[OPNData]
SET
[Value] = #total
WHERE NomSubmittedDate between DATEADD(HH, 6, DATEDIFF(dd, 0, GETDATE()))
and DATEADD(HH, 5, DATEDIFF(dd, 0, GETDATE()+1))
GO

Help with a SQL Query

I am wanting to perform a Threshold query, whereby one would take the Value of a field from Today, and compare it to the value from yesterday, this query would look something like this, but obviously, it is wrong, and I can't find how to do it:
select
TableB.Name,
TableA.Charge,
(
select Charge
from TableA
where (DateAdded >= '13/10/2009' and DateAdded < '14/10/2009')
)
from TableA
inner join
TableB on TableB.ID = TableA.ID
where
TableA.DateAdded >= '10/14/2009'
order by
Name asc
Just a quick note, I am looking for two CHARGE fields, not the dates. The date manipulation is simply for Today and Yesterday, nothing more.
At the end of this, I want to do a calculation on the two returned charge fields, so if its easier to show that, that would also be great.
Thanks in advance
Kyle
EDIT1:
The data I am looking for is like so:
Yesterday, we input a charge of 500 to MachineA
Today we input a charge of 300 to MachineA
We run the query, and results I need are as follows:
Name = MachineA
Charge = 300
YesterdayCharge = 500
If you really need previous date (including weekends etc), then following query should do the job. Otherwise please post data samples and expected results:
SELECT TableB.Name,
TableA.Charge,
prev.Charge AS PrevCharge
FROM TableA
INNER JOIN TableB
ON TableA.ID = TableB.ID
LEFT JOIN TableA prev
ON TableA.ID = prev.ID
--// use this if DateAdded contains only date
--AND TableA.DateAdded = DATEADD(day, +1, prev.dateAdded)
--// use this if DateAdded contains also time component
AND CONVERT(DATETIME, CONVERT(CHAR(8), TableA.DateAdded, 112), 112) = DATEADD(day, +1, CONVERT(DATETIME, CONVERT(CHAR(8), prev.dateAdded, 112), 112))
edit-1: added option in JOIN for cases when DateAdded contains time as well
something like this?
SELECT
B.Name,
A.Charge,
DATEPART(day, A.DateAdded) as day
FROM
TableA A, Table B
WHERE
B.ID = A.ID AND
A.DateAdded BETWEEN DATEADD(day, -1, GETDATE()) AND GETDATE()
GROUP BY
B.Name,
A.Charge,
A.DateAdded
Try this:
DECLARE #ValuesTable TABLE(Name VARCHAR(20), Charge INT, DateAdded DATETIME)
INSERT INTO #ValuesTable
SELECT 'Name1', 10, DATEADD(dd, 2, DATEDIFF(dd, 0, GETDATE())) UNION
SELECT 'Name2', 20, DATEADD(dd, 2, DATEDIFF(dd, 0, GETDATE())) UNION
SELECT 'Name1', 30, DATEADD(dd, 1, DATEDIFF(dd, 0, GETDATE())) UNION
SELECT 'Name2', 40, DATEADD(dd, 1, DATEDIFF(dd, 0, GETDATE())) UNION
SELECT 'Name1', 50, DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) UNION
SELECT 'Name2', 60, DATEADD(dd, 0, DATEDIFF(dd, 0, GETDATE())) UNION
SELECT 'Name1', 70, DATEADD(dd, -1, DATEDIFF(dd, 0, GETDATE())) UNION
SELECT 'Name3', 80, DATEADD(dd, -1, DATEDIFF(dd, 0, GETDATE())) UNION
SELECT 'Name1', 90, DATEADD(dd, -2, DATEDIFF(dd, 0, GETDATE())) UNION
SELECT 'Name2', 100, DATEADD(dd, -2, DATEDIFF(dd, 0, GETDATE()))
SELECT
ISNULL(T.Name,Y.Name) AS Name,
SUM(ISNULL(Y.Charge,0)) AS Yesterday, SUM(ISNULL(T.Charge,0)) AS Today,
SUM(ISNULL(T.Charge,0)) - SUM(ISNULL(Y.Charge,0)) AS Diff
FROM(
SELECT Name, Charge
FROM #ValuesTable
WHERE DateAdded BETWEEN DATEADD(day, -2, GETDATE())
AND DATEADD(day, -1, GETDATE())
) AS Y
FULL JOIN(
SELECT Name, Charge
FROM #ValuesTable
WHERE DateAdded BETWEEN DATEADD(day, -1, GETDATE()) AND GETDATE()
) AS T ON ISNULL(T.Name,Y.Name) = ISNULL(Y.Name,T.Name)
GROUP BY ISNULL(T.Name,Y.Name) , ISNULL(Y.Name,T.Name)
It may be just a typo in SO but if you are using date strings '14/10/2009' and '10/14/2009' in the same query it will never work.
No matter which date format you are using one of them has too many months in it.