Results being returned in multiple rows - sql

I have created a stored procedure that is run at the end of every day which will return the attendance percentage of each student in a class, the result set will show the students previous attendance percentage compared against their new percentage. But for some reason the results are being stored in multiple rows instead of one row. The diagrams below will give you a better understanding of the problem.
The Two Tables:
Student Table
Percentage Table
The code I have written to get the results into a combined table:
select
StudentTb.StudentId,
StudentTb.Forename,
StudentTb.Surname,
CONVERT(VARCHAR(10),DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE())-1, 0),103) as [Previous Reading Date]
case studentTb.Date
when DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE())-1, 0) then PercentageTb.Percentage
End
'Previous Percentage'
CONVERT(VARCHAR,DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0),103) as [Present Reading Date]
case studentTb.Date
when DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE())-1, 0) then PercentageTb.Percentage
End
'Current Percentage'
from studentTb inner join
PercentageTb on studentTb.StudentID = PercentageTb.StudentID
and this is the result set from this query!
However this is not the way I intended the results to look, below shows the desired result set!
I thought the CASE statement would have done that for me but obviously I was wrong, could someone please give me some indication on where I need to go from here?

Why not just do a select with sub select? Assuming a student can only have one result on each day
DECLARE #CurrentDate DATE
DECLARE #PreviousDate DATE
SET #CurrentDate =
SET #PreviousDate =
SELECT
StudentTb.StudentId AS [ID],
StudentTb.Forename AS [Forename],
StudentTb.Surname AS [Surname],
(SELECT Percentage from PercentageTb where StudentID = S.studentID and Date = #PreviousDate) AS [PreviousPercentage]
(SELECT Percentage from PercentageTb where StudentID = S.studentID and Date = #CurrentDate) AS [CurrentPercentage]
from studentTb AS S

One way to accomplish what you want that should work is to use the max() aggregate function to flatten the results like this:
select
s.StudentID,
s.Forename,
s.Surname,
max(case when p.date = CONVERT(VARCHAR,DATEADD(DAY, DATEDIFF(DAY, 1, GETDATE()), 0),103) then p.date end) prev_date,
max(case when p.date = CONVERT(VARCHAR,DATEADD(DAY, DATEDIFF(DAY, 1, GETDATE()), 0),103) then p.percentage end) prev_perc,
max(case when p.date = CONVERT(VARCHAR,DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0),103) then p.date end) curr_date,
max(case when p.date = CONVERT(VARCHAR,DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0),103) then p.percentage end) curr_perc
from studentTb s
inner join percentageTb p on s.StudentID = p.StudentID
group by s.StudentID, s.Forename, s.Surname
Sample SQL Fiddle
Another option would be to join the percentageTb table twice (once for the current date and once for the previous day), see this example.

Related

a query displaying 0 instead of not showing

the following query is displaying the result i want except i want it to show 0 for each month with non production.
SELECT
DATENAME(MONTH, DATEADD(M, MONTH(PolicyDetails.IssuedDate), - 1)) AS Month,
SUM(PolicyDetails.Premium) AS TotalProduction,
DATENAME(YEAR, PolicyDetails.IssuedDate) AS Year
FROM PolicyDetails INNER JOIN Clients
ON PolicyDetails.ClientId = Clients.ClientId
WHERE (Clients.Username = #Username)
GROUP BY MONTH(PolicyDetails.IssuedDate), DATENAME(YEAR, PolicyDetails.IssuedDate)
Month Total Production -$$
2019 - August 30.00
2019 - October 45.00
in this table i want to show "2019 - September" with Total Production = 0 instead of displaying nothing. How ??
If you want to show all months in the data, probably the simplest method is to use conditional aggregation. Your calculation of the month seems awkward. You seem to want the previous month, so:
SELECT DATENAME(YEAR, pd.IssuedDate) AS Year,
DATENAME(MONTH, DATEADD(MONTH, -1, pd.IssuedDate)) AS Month,
SUM(CASE WHEN c.Username = #Username THEN pd.Premium ELSE 0 END) AS TotalProduction
FROM PolicyDetails pd INNER JOIN
Clients c
ON pd.ClientId = c.ClientId
GROUP BY DATENAME(YEAR, pd.IssuedDate), DATENAME(MONTH, DATEADD(MONTH, -1, pd.IssuedDate))
ORDER BY MIN(pd.IssuedDate)
This assumes that you have at least one row per month in the data.
Otherwise, the canonical approach is to generate the months you want (using a derived table or recursive CTE or calendar table). Your month arithmetic is a bit awkward for that solution. It would look like:
SELECT YEAR(DATEADD(MONTH, -1, months.mstart)),
MONTH(DATEADD(MONTH, -1, months.mstart)),
COALESCE(SUM(pd.Premium), 0) AS TotalProduction
FROM (VALUES (CONVERT(DATE, '2019-08-01')),
(CONVERT(DATE, '2019-09-01')),
(CONVERT(DATE, '2019-10-01'))
) months(mstart) LEFT JOIN
PolicyDetails pd
ON pd.IssuedDate >= DATEADD(month, -1, months.mstart) AND
pd.IssuedDate < months.mstart LEFT JOIN
Clients c
ON pd.ClientId = c.ClientId AND
c.Username = #Username
GROUP BY YEAR(DATEADD(MONTH, -1, months.mstart)),
MONTH(DATEADD(MONTH, -1, months.mstart))
ORDER BY MIN(pd.IssuedDate)
I suppose that might be a usecase for SQL-IF.
Syntax as follows:
SELECT IF([condition], "YES", "NO");
In your case, try to combine that with your query.
Maybe like this:
SELECT IF([subquery > 0] AS X, X, 0);
If-Condition Like:
SELECT IF((SELECT COUNT(*) FROM PolicyDetails ) > 0, 1, 0);
But you will have to combine that output with your existing query posted above, to gain the output if the table is filled.

How to return next year values when you're in the current year, but keep current values when you switch to next year? which becomes current year

I have a view that runs a user selects a certain month. It returns various values based on a month selection. Using MS SQL Server
Problem:
It returns those values based on the month and it works as it should up until a user chooses January. When January is chosen, it brings back both this current year (which has already passed) and this upcoming January (January 2020. Which is what they want). How do I remove the 2019 values when next year is chosen, but also keep them when the year changes to the next year?
Here is the query
SELECT distinct
RRP.Id, RRP.UniqClient, RRP.Client, RRP.Expiring_Policies, RRP.UniqBroker,
CONVERT(varchar, RRP.ExpDate, 101) AS ExpDate, RRP.NewUw, RRP.OtherContact,
RRP.Company, RRP.Broker_Name, RRP.Reviewed, RRP.DateCreated, RRP.DateUpdated,
RRP.EmailCreated, RRP.UniqProducer, RRP.LINES,
CASE WHEN MMS.UniqMarketingSubmission IS NULL THEN 'No' ELSE 'Yes' END AS [MMS Created]
FROM dbo.Reviewed_Renewal_Policy AS RRP LEFT OUTER JOIN
GREEN43_PROD_REPL.dbo.MarketingSubmission AS MMS ON RRP.UniqClient = MMS.UniqEntity AND RRP.ExpDate = MMS.EffectiveDate
where (RRP.Reviewed = 'No') AND (DATENAME(month, RRP.ExpDate) = DATENAME(MONTH, DATEADD(Month, 3, GETDATE())))
order by ExpDate ASC
Typically there is a .NET webforms variable in place of the 3 in DATEADD, but the 3 works in SQL and I kept it for clarity
I have tried messing with comparing current dates and adding a year, but nothing successful so far. Thanks again for your help.
You can add another condition in where cause, try it.
AND YEAR(RRP.ExpDate) = YEAR(GETDATE())
Thanks
If you want the value for three months in the future, I would suggest:
where RRP.Reviewed = 'No' and
RRP.ExpDate >= datefromparts(year(dateadd(month, 3, getdate()),
month(dateadd(month, 3, getdate()),
1) and
RRP.ExpDate < datefromparts(year(dateadd(month, 4, getdate()),
month(dateadd(month, 4, getdate()),
1)
Note: This is constructed so it can use an index on (Reviewed, ExpDate) or (ExpDate).
Just as an example of how much easier it is to read formatted sql. I took the OPs posted answer and formatted it a bit. Removed several extra sets of parenthesis and moved around a little logic so it was a bit simpler.
SELECT distinct RRP.Id
, RRP.UniqClient
, RRP.Client
, RRP.Expiring_Policies
, RRP.UniqBroker
, CONVERT(varchar, RRP.ExpDate, 101) AS ExpDate
, RRP.NewUw
, RRP.OtherContact
, RRP.Company
, RRP.Broker_Name
, RRP.Reviewed
, RRP.DateCreated
, RRP.DateUpdated
, RRP.EmailCreated
, RRP.UniqProducer
, RRP.LINES
, CASE WHEN MMS.UniqMarketingSubmission IS NULL THEN 'No' ELSE 'Yes' END AS [MMS Created]
FROM dbo.Reviewed_Renewal_Policy AS RRP
LEFT OUTER JOIN GREEN43_PROD_REPL.dbo.MarketingSubmission AS MMS ON RRP.UniqClient = MMS.UniqEntity
AND RRP.ExpDate = MMS.EffectiveDate
where RRP.Reviewed = 'No'
AND DATENAME(month, RRP.ExpDate) = DATENAME(MONTH, DATEADD(Month, 4, GETDATE()))
AND
(
Month(getdate()) <= Month(RRP.ExpDate)
)
OR
(
Month(getdate()) > Month(RRP.ExpDate)
AND
Year(RRP.ExpDate) > Year(getdate())
)
order by ExpDate ASC
Appreciate the help I was given. I managed to answer my own question. I found an example of this solution from another question. I modified it to fit my own.
SELECT distinct
RRP.Id, RRP.UniqClient, RRP.Client, RRP.Expiring_Policies, RRP.UniqBroker, CONVERT(varchar, RRP.ExpDate, 101) AS ExpDate,
RRP.NewUw, RRP.OtherContact, RRP.Company, RRP.Broker_Name, RRP.Reviewed, RRP.DateCreated,
RRP.DateUpdated, RRP.EmailCreated, RRP.UniqProducer, RRP.LINES, CASE WHEN MMS.UniqMarketingSubmission IS NULL THEN 'No' ELSE 'Yes' END AS [MMS Created]
FROM dbo.Reviewed_Renewal_Policy AS RRP LEFT OUTER JOIN
GREEN43_PROD_REPL.dbo.MarketingSubmission AS MMS ON RRP.UniqClient = MMS.UniqEntity AND RRP.ExpDate = MMS.EffectiveDate
where ((RRP.Reviewed = 'No') AND (DATENAME(month, RRP.ExpDate) = DATENAME(MONTH, DATEADD(Month, 4, GETDATE())))
AND Month(getdate()) <= Month(RRP.ExpDate))
OR
((RRP.Reviewed = 'No') AND (DATENAME(month, RRP.ExpDate) = DATENAME(MONTH, DATEADD(Month, 4, GETDATE())))
AND Month(getdate()) > Month(RRP.ExpDate) AND Year(RRP.ExpDate) > Year(getdate()))
order by ExpDate ASC
This brings back the current year values and when the next year values when the appropriate selection is made.
OR an Alternative thanks to Gordon Linoff above
SELECT distinct
RRP.Id, RRP.UniqClient, RRP.Client, RRP.Expiring_Policies, RRP.UniqBroker, CONVERT(varchar, RRP.ExpDate, 101) AS ExpDate,
RRP.NewUw, RRP.OtherContact, RRP.Company, RRP.Broker_Name, RRP.Reviewed, RRP.DateCreated,
RRP.DateUpdated, RRP.EmailCreated, RRP.UniqProducer, RRP.LINES, CASE WHEN MMS.UniqMarketingSubmission IS NULL THEN 'No' ELSE 'Yes' END AS [MMS Created]
FROM dbo.Reviewed_Renewal_Policy AS RRP LEFT OUTER JOIN
GREEN43_PROD_REPL.dbo.MarketingSubmission AS MMS ON RRP.UniqClient = MMS.UniqEntity AND RRP.ExpDate = MMS.EffectiveDate
where ((RRP.Reviewed = 'No') AND (DATENAME(month, RRP.ExpDate) = DATENAME(MONTH, DATEADD(Month, 3, GETDATE())))
AND Month(getdate()) <= Month(RRP.ExpDate))
OR
((RRP.Reviewed = 'No') AND (DATENAME(month, RRP.ExpDate) = DATENAME(MONTH, DATEADD(Month, 3, GETDATE()))) AND
datefromparts(1 + year(getdate()), 1, 1) <= RRP.ExpDate and datefromparts(1 + year(getdate()), 2, 1) >= RRP.ExpDate)
order by ExpDate ASC

Is it possible to add second where condition to select this same data but from other date range?

I have two tables in SQL Server.
I want to select DeptCode, DeptName, YearToDate, PeriodToDate (2 months for example) and group it by DeptCode.
There is a result which I want to get:
In YTD column I want to get sum of totalCost since 01/01/actualYear.
In PTD column I want to get the sum from last two months.
I created a piece of code which shows me correct YTD cost but I don't know how I can add next one for getting total cost for other date range. Is it possible to do this?
SELECT
d.DeptCode,
d.DeptName,
SUM(s.TotalCost) as YTD
FROM [Departments] AS d
INNER JOIN Shipments AS s
ON d.DeptCode= s.DeptCode
WHERE s.ShipmentDate BETWEEN DateAdd(yyyy, DateDiff(yyyy, 0, GetDate()), 0)
AND GETDATE()
GROUP BY d.DeptCode, d.DeptName
Your expected output doesn't match 2 months, but here's the code to accomplish what you want. You just have to add a SUM(CASE...) on the 2nd condition.
SELECT
d.DeptCode,
d.DeptName,
SUM(s.TotalCost) as YTD,
SUM(CASE WHEN s.ShipmentDate >= DATEADD(month, -2, GETDATE()) then s.TotalCost else 0 END) as PTD
FROM [Departments] AS d
INNER JOIN Shipments AS s
ON d.DeptCode= s.DeptCode
WHERE Year(s.ShipmentDate) = Year(GETDATE())
GROUP BY d.DeptCode, d.DeptName
Just add one more column that returns 0 when not in the two-month range, e.g. SUM(CASE WHEN (date check) THEN (amount) ELSE 0 END). Check out the fifth line:
SELECT
d.DeptCode,
d.DeptName,
SUM(s.TotalCost) as YTD,
SUM(CASE WHEN DateDiff(MONTH, s.ShipmentDate, GetDate()) < 2 THEN s.TotalCost ELSE 0 END) PTD,
FROM [Departments] AS d
INNER JOIN Shipments AS s
ON d.DeptCode= s.DeptCode
WHERE s.ShipmentDate BETWEEN DateAdd(yyyy, DateDiff(yyyy, 0, GetDate()), 0)
AND GETDATE()
GROUP BY d.DeptCode, d.DeptName
Try this one :
nbr_last2month_ AS
(
SELECT DISTINCT
Sum(s.[TotalCost]) AS 'PTD',
s.DeptCode,
s.DeptName
FROM [Shipements] s
LEFT JOIN [Departements] d ON d.[DeptCode] = s.[DeptCode]
WHERE Year(date_) LIKE Year(GETDATE())
AND MONTH(ShipementDate) LIKE Month(Getdate()) - 2
Group by DeptCode
),
nbr_YTD_ AS
(
SELECT DISTINCT
Sum(s.[TotalCost]) AS 'YTD',
s.DeptCode,
s.DeptName
FROM [Shipements] s
LEFT JOIN [Departements] d ON d.[DeptCode] = s.[DeptCode]
WHERE Year(ShipementDate) LIKE Year(GETDATE())
Group by DeptCode
),
SELECT
A.DeptCode,
A.DeptName,
YTD,
PTD
FROM nbr_YTD_ A
LEFT JOIN nbr_last2month_ B on B.DeptCode = A.DeptCode
ORDER BY DeptCode

How to select Months for data without any data existing in SQL

I have a line chart/graph that I need to include for all service outages we went through in the year. Currently, my SQL can pull data that includes any tickets creating in a ticketing system for these services.
However, if no ticket was created for a service, the service will not show in the rows or the service will show but not have a 100% Uptime for the months where no tickets were created for it.
Example of what I am looking for:
MONTH Service1 Service2 Service3
1 100% 99.7% 100%
2 99.8% 100% 96.5%
3 100% 99.8% 100%
But what it looks like is this:
MONTH Service1 Service2 Service3
1 99.7%
2 99.8% 96.5%
3 99.8%
The services get pulled by using a WHERE [Resource]='Affected Service' so they are dynamically brought into the table, but no data is pulled for the service if no ticket was created in that month.
Current SQL Coding:
WITH rslt (ResourceID, YearNumber, MonthNumber, AvailableMinutes, DowntimeMinutes) AS (
SELECT
ore.ResourceID,
DATEPART(yyyy, ipr.OpenDate_MST) YearNumber,
DATEPART(mm, ipr.OpenDate_MST) MonthNumber,
MAX(CASE
WHEN DATEADD(MONTH, DATEDIFF(MONTH, -1, ipr.OpenDate_MST), -1) = DATEADD(MONTH, DATEDIFF(MONTH, -1, GETDATE()), -1)
THEN (DATEPART(DD, GETDATE()) * 1440.0)
ELSE (DATEPART(DD, DATEADD(MONTH, DATEDIFF(MONTH, -1, ipr.OpenDate_MST), -1)) * 1440.0)
END) AvailableMinutes,
ISNULL(SUM(DATEDIFF(mi, ipr.OutageStartTime, ipr.OutageEndTime)), 0) DowntimeMinutes
FROM
vIncidentProblemRequest ipr
INNER JOIN vOwnedResource ore ON ore.ResourceID = ipr.AffectedServiceID
WHERE
CONVERT(DATETIME, CONVERT(CHAR(10), ipr.OpenDate_MST, 101)) >= '1/1/2013 12:00:00'
AND CONVERT(DATETIME, CONVERT(CHAR(10), ipr.OpenDate_MST, 101)) <= '12/31/2013 11:59:59'
GROUP BY
ore.ResourceID,
DATEPART(yyyy, ipr.OpenDate_MST),
DATEPART(mm, ipr.OpenDate_MST),
),
rslt2 (ResourceID, Application, ResourceClass, YearNumber, MonthNumber, AvailableMinutes, DowntimeMinutes, UptimePercent) AS (
SELECT
ore.ResourceID,
ore.ResourceName Application,
ore.ResourceClass,
rslt.YearNumber,
rslt.MonthNumber,
rslt.AvailableMinutes,
ISNULL(rslt.DowntimeMinutes, 0) DowntimeMinutes,
CASE
WHEN rslt.DowntimeMinutes IS NULL
THEN 1.0
ELSE ((rslt.AvailableMinutes - rslt.DowntimeMinutes)/rslt.AvailableMinutes)
END UptimePercent
FROM
vOwnedResource ore
LEFT OUTER JOIN rslt ON rslt.ResourceID = ore.ResourceID
WHERE
ore.ResourceClass = 'Enterprise Service')
select
MIN(DATEPART(yyyy, d.Date)) Year,
MIN(DATEPART(mm, d.Date)) MonthNum,
SUBSTRING(MIN(DATENAME(mm, d.Date)), 1, 3) Month,
r.Application,
r.ResourceClass,
CASE
WHEN r.UptimePercent IS NULL
THEN 1.0
ELSE r.UptimePercent
END UptimePercent
FROM
DimDate d
INNER JOIN rslt2 r ON r.YearNumber = datepart(yyyy, d.Date) AND r.MonthNumber = datepart(mm, d.Date)
WHERE
d.Date >= '1/1/2013 12:00:00'
AND d.Date <= '12/31/2013 11:59:59'
GROUP BY
datepart(yyyy, d.Date),
datepart(mm, d.Date),
DATENAME(mm, d.Date),
r.Application,
r.ResourceClass,
r.UptimePercent
ORDER BY
4,1,2
How about this-
Create a temp table having all service values as 100% for each month.
After that, update the temp table with the real values based on your filter condition.
It would really help if you showed the query. However, somewhere along the way, you can solve your problem using coalesce(). Assuming the values are really numbers between 0 and 100:
select month, coalesce(service1, 100) as service1,
coalesce(service2, 100) as service2, coalesce(service3, 100) as service3
The following answer describes the 'EXISTS' command. I think it will help you greatly.
Using CASE to Return a String If No Results From SELECT Statement

Changing comparator in WHERE clause has catastrophic results on query performance

I have a monster query that I'm running against a SQL SERVER 2005 database that is acting very strange. I have two conditions in the WHERE clause of the outermost select, comparing a field to a constant date. When the constant dates are either identical (down to the second) or their date parts are not equal, the query runs in under 2 seconds. When the date parts are the same but the time parts are different, the query takes around 7 minutes to complete. Specifically, having a WHERE clause of
WHERE
d.date >= '2011-11-07 00:00:00' AND
d.date <= '2011-11-08 11:59:59'
works well and as expected. Changing the WHERE clause to
WHERE
d.date >= '2011-11-07 00:00:00' AND
d.date <= '2011-11-07 11:59:59'
causes the query to take many minutes.
I also noticed that when I turned off the index on the Agent_Hours table that the bad case of having the same dates the same reduces the query time to 25 seconds, still far longer than when they dates are different, but not by as much.
Below is the full query for reference (the WHERE clause in question is at the very end):
SELECT
s.transaction_id AS 'transaction',
s.created_on AS transaction_date,
s.first_name + ' ' + s.Last_Name AS customer_name,
a.name AS agent_name,
a.phantom AS phantom,
a.team AS agent_team,
a.id AS agent_number,
h.hours,
h2.hours_today,
d.*
FROM
(SELECT
agents.first_name + ' ' + agents.last_name AS name,
agents.id AS id,
agents.phantom AS phantom,
transient.value AS team,
transient.start_date AS team_start_date,
transient.end_date AS team_end_date
FROM
Agents.dbo.Agent_Static AS agents
JOIN
Agents.dbo.Agent_Transient AS transient
ON transient.agent = agents.id
WHERE
transient.field = 'team') AS a
LEFT JOIN Agents.dbo.Agent_Daily AS d
ON d.agent = a.id
LEFT JOIN (SELECT
agent_hours.agent AS agent,
dates.date AS date,
CAST(COUNT(*) AS FLOAT) / 4 AS hours
FROM
Agents.dbo.Agent_Hours AS agent_hours
JOIN
(SELECT
DISTINCT CONVERT(
VARCHAR(10),
hour_worked,
101)
AS date
FROM
Agents.dbo.Agent_Hours) AS dates
ON dates.date = CONVERT(
VARCHAR(10),
agent_hours.hour_worked,
101)
WHERE
(status = 'Phone' OR
status = 'Meeting')
GROUP BY
agent_hours.agent,
dates.date) AS h
ON h.agent = a.id AND
h.date = d.date
LEFT JOIN (SELECT
agent_hours.agent AS agent,
dates.date AS date,
CAST(COUNT(*) AS FLOAT) / 4 AS hours_today
FROM
Agents.dbo.Agent_Hours AS agent_hours
JOIN
(SELECT
DISTINCT CONVERT(
VARCHAR(10),
hour_worked,
101)
AS date
FROM
Agents.dbo.Agent_Hours) AS dates
ON dates.date = CONVERT(
VARCHAR(10),
agent_hours.hour_worked,
101)
WHERE
(status = 'Phone' OR
status = 'Meeting') AND
CONVERT(
VARCHAR(10),
CAST('11/09/2011 13:01' AS DATETIME),
101) = CONVERT(
VARCHAR(10),
agent_hours.hour_worked,
101) AND
CONVERT(
VARCHAR(10),
CAST('11/09/2011 13:01' AS DATETIME),
114) > CONVERT(
VARCHAR(10),
agent_hours.hour_worked,
114)
GROUP BY
agent_hours.agent,
dates.date) AS h2
ON h2.agent = a.id AND
h2.date = d.date
LEFT JOIN sale_transactions AS s
ON a.id = s.agent_hermes_id AND
s.created_on >= a.team_start_date AND
s.created_on <= a.team_end_date AND
CONVERT(
VARCHAR(10),
d.date,
101) = CONVERT(
VARCHAR(10),
s.created_on,
101)
LEFT JOIN sold_phrases AS p
ON s.Transaction_ID = p.transaction_id
WHERE
d.date >= '2011-11-07 00:00:00' AND
d.date <= '2011-11-07 11:59:59'
As a general rule, always post your exact table definition, including all indexes, when asking performance problems in SQL.
I cannot see any difference between the two cases, but considering your explanation, this is what likely happens: the cardinality estimates for the date range may trigger the index tipping point and you get wildly different execution plans. Such issues are best addressed by using plan guides, see Optimizing Queries in Deployed Applications by Using Plan Guides. You should be able to confirm if the problem is indeed the plan, see Displaying Graphical Execution Plans (SQL Server Management Studio).
This is maybe a micro optimization but have you consider changing the way you get the date part from datetime to DATEADD(dd, 0, DATEDIFF(dd, 0, datetime_format)). It's usually faster way than convert function.
SELECT
s.transaction_id AS 'transaction',
s.created_on AS transaction_date,
s.first_name + ' ' + s.Last_Name AS customer_name,
a.name AS agent_name,
a.phantom AS phantom,
a.team AS agent_team,
a.id AS agent_number,
h.hours,
h2.hours_today,
d.*
FROM (SELECT
agents.first_name + ' ' + agents.last_name AS name,
agents.id AS id,
agents.phantom AS phantom,
transient.value AS team,
transient.start_date AS team_start_date,
transient.end_date AS team_end_date
FROM
Agents.dbo.Agent_Static AS agents
JOIN
Agents.dbo.Agent_Transient AS transient
ON transient.agent = agents.id
WHERE
transient.field = 'team'
) AS a
LEFT JOIN Agents.dbo.Agent_Daily AS d ON d.agent = a.id
LEFT JOIN (
SELECT
agent_hours.agent AS agent,
dates.date AS date,
COUNT(*) / 4.0 AS hours
FROM Agents.dbo.Agent_Hours AS agent_hours
JOIN (
SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, hour_worked)) as date
FROM Agents.dbo.Agent_Hours GROUP BY DATEADD(dd, 0, DATEDIFF(dd, 0, hour_worked))
) AS dates ON dates.date = DATEADD(dd, 0, DATEDIFF(dd, 0, agent_hours.hour_worked))
WHERE (status = 'Phone' OR status = 'Meeting')
GROUP BY agent_hours.agent, dates.date
) AS h ON h.agent = a.id AND h.date = d.date
LEFT JOIN (
SELECT
agent_hours.agent AS agent,
dates.date AS date,
COUNT(*) / 4.0 AS hours_today
FROM Agents.dbo.Agent_Hours AS agent_hours
JOIN (
SELECT DATEADD(dd, 0, DATEDIFF(dd, 0, hour_worked)) as date
FROM Agents.dbo.Agent_Hours GROUP BY DATEADD(dd, 0, DATEDIFF(dd, 0, hour_worked))
) AS dates ON dates.date = DATEADD(dd, 0, DATEDIFF(dd, 0, agent_hours.hour_worked))
WHERE
(status = 'Phone' OR status = 'Meeting') AND
agent_hours.hour_worked >=
DATEADD(dd, 0, DATEDIFF(dd, 0, CAST('11/09/2011 13:01' AS DATETIME)))
AND
agent_hours.hour_worked <
CAST('11/09/2011 13:01' AS DATETIME)
GROUP BY agent_hours.agent, dates.date
) AS h2 ON h2.agent = a.id AND h2.date = d.date
LEFT JOIN sale_transactions AS s
ON a.id = s.agent_hermes_id AND
s.created_on >= a.team_start_date AND
s.created_on <= a.team_end_date AND
DATEADD(dd, 0, DATEDIFF(dd, 0, d.date))
=
DATEADD(dd, 0, DATEDIFF(dd, 0, s.created_on))
LEFT JOIN sold_phrases AS p
ON s.Transaction_ID = p.transaction_id
WHERE
d.date >= '2011-11-07 00:00:00' AND
d.date <= '2011-11-07 11:59:59'
The more important (as Remus Rusanu already wrote) are indexes. Execute both queries and check which indexes are used in faster query and force SQL Server to use them always. You can do it using with(index(index_name)).