Tough T-SQL To Left Join? - sql-server-2005

I've got a table of ExchangeRates that have a countryid and an exchangeratedate something to this effect:
ExchangeRateID Country ToUSD ExchangeRateDate
1 Euro .7400 2/14/2011
2 JAP 80.1900 2/14/2011
3 Euro .7700 7/20/2011
Notice there can be the same country with a different rate based on the date...so for instance above Euro was .7400 on 2/14/2011 and now is .7700 7/20/2011.
I have another table of line items to list items based on the country..in this table each line item has a date associated with it. The line item date should use the corresponding date and country based on the exchange rate. So using the above data if I had a line item with country Euro on 2/16/2011 it should use the euro value for 2/14/2011 and not the value for 7/20/2011 because of the date (condition er.ExchangeRateDate <= erli.LineItemDate). This would work if I only had one item in the table, but imagine I had a line item date of 8/1/2011 then that condition (er.ExchangeRateDate <= erliLineItemDate) would return multiple rows hence my query would fail...
SELECT
er.ExchangeRateID,
er.CountryID AS Expr1,
er.ExchangeRateDate,
er.ToUSD,
erli.ExpenseReportLineItemID,
erli.ExpenseReportID,
erli.LineItemDate
FROM
dbo.ExpenseReportLineItem AS erli
LEFT JOIN
dbo.ExchangeRate AS er
ON er.CountryID = erli.CountryID
AND DATEADD(d, DATEDIFF(d, 0, er.ExchangeRateDate), 0) <= DATEADD(d, DATEDIFF(d, 0,
erli.LineItemDate), 0)
WHERE (erli.ExpenseReportID = 196)
The issue with this left join...is because the dates are <= the line item date so it returns many records, I would have to somehow do this but dont know how.
The LineItem tables has multiple records and each record could have its own CountryID:
Item Country ParentID LineItemDate
Line Item 1 Euro 1 2/14/2011
Line Item 2 US 1 2/14/2011
Line Item3 Euro 1 2/15/2011
So there are three records for ParentID (ExpenseReportID) = 1. So then I take those records and join the ExchangeRate table where the Country in my line item table = the country of the exchange rate table (that part is easy) BUT the second condition I have to do is the:
AND DATEADD(d, DATEDIFF(d, 0, er.ExchangeRateDate), 0) <= DATEADD(d, DATEDIFF(d, 0,
erli.LineItemDate), 0)
But here is where the issue is because that will return multiple rows from my exchange rate table because euro is listed twice.

I may be missing something here, but as I understand it the "dumb" solution to your problem is to use A ROW_NUMBER function and outer filter with your existing "returns too many entries" query (this can also be done with a CTE, but I prefer the derived table syntax for simple cases like this):
SELECT *
FROM (
SELECT
er.ExchangeRateID,
er.CountryID AS Expr1,
er.ExchangeRateDate,
er.ToUSD,
erli.ExpenseReportLineItemID,
erli.ExpenseReportID,
erli.LineItemDate,
ROW_NUMBER() OVER (PARTITION BY ExpenseReportID, ExpenseReportLineItemID ORDER BY ExchangeRateDate DESC) AS ExchangeRateOrderID
FROM dbo.ExpenseReportLineItem AS erli
LEFT JOIN dbo.ExchangeRate AS er
ON er.CountryID = erli.CountryID
AND DATEADD(d, DATEDIFF(d, 0, er.ExchangeRateDate), 0)
<= DATEADD(d, DATEDIFF(d, 0, erli.LineItemDate), 0)
WHERE (erli.ExpenseReportID = 196)
--For reasonable performance, it would be VERY nice to put a filter
-- on how far back the exchange rates can go here:
--AND er.ExchangeRateDate > DateAdd(Day, -7, GetDate())
) As FullData
WHERE ExchangeRateOrderID = 1
Sorry if I misunderstood, otherwise hope this helps!

It would make your life a lot easier if you could add an additional column to your ExchangeRates table called (something like)
ExchangeRateToDate
A separate process could update the previous entry when a new one was added.
Then, you could just query for LineItemDate >= ExhangeRateDate and <= ExchangeRateToDate
(treating the last one, presumably with a null ExchangeRateToDate, as a special case).

I would create an in memory table creating an ExchangeRate table with ExchangeRateDates From & To.
All that's left to do after this is joining this CTE in your query instead of your ExchangeRate table and add a condition where the date is betweenthe date from/to.
SQL Statement
;WITH er AS (
SELECT rn = ROW_NUMBER() OVER (PARTITION BY er1.ExchangeRateID ORDER BY er2.ExchangeRateDate DESC)
, er1.ExchangeRateID
, er1.Country
, ExchangeRateDateFrom = ISNULL(DATEADD(d, 1, er2.ExchangeRateDate), 0)
, ExchangeRateDateTo = er1.ExchangeRateDate
, er1.ToUSD
FROM #ExchangeRate er1
LEFT OUTER JOIN #ExchangeRate er2
ON er1.Country = er2.Country
AND er1.ExchangeRateDate >= er2.ExchangeRateDate
AND er1.ExchangeRateID > er2.ExchangeRateID
)
SELECT er.ExchangeRateID,
er.CountryID AS Expr1,
er.ExchangeRateDateTo,
er.ToUSD,
erli.ExpenseReportLineItemID,
erli.ExpenseReportID,
erli.LineItemDate
FROM dbo.ExpenseReportLineItem AS erli
LEFT JOIN er ON er.CountryID = erli.CountryID
AND DATEADD(d, DATEDIFF(d, 0, er.ExchangeRateDateTo), 0) <= DATEADD(d, DATEDIFF(d, 0, erli.LineItemDate), 0)
AND DATEADD(d, DATEDIFF(d, 0, er.ExchangeRateDateFrom), 0) >= DATEADD(d, DATEDIFF(d, 0, erli.LineItemDate), 0)
WHERE (erli.ExpenseReportID = 196)
and er.rn = 1
Test script
DECLARE #ExchangeRate TABLE (
ExchangeRateID INTEGER
, Country VARCHAR(32)
, ToUSD FLOAT
, ExchangeRateDate DATETIME
)
INSERT INTO #ExchangeRate
VALUES (1, 'Euro', 0.7400, '02/14/2011')
, (2, 'JAP', 80.1900, '02/14/2011')
, (3, 'Euro', 0.7700, '07/20/2011')
, (4, 'Euro', 0.7800, '07/25/2011')
;WITH er AS (
SELECT rn = ROW_NUMBER() OVER (PARTITION BY er1.ExchangeRateID ORDER BY er2.ExchangeRateDate DESC)
, er1.ExchangeRateID
, er1.Country
, ExchangeRateDateFrom = ISNULL(DATEADD(d, 1, er2.ExchangeRateDate), 0)
, ExchangeRateDateTo = er1.ExchangeRateDate
, ToUSD = er1.ToUSD
FROM #ExchangeRate er1
LEFT OUTER JOIN #ExchangeRate er2
ON er1.Country = er2.Country
AND er1.ExchangeRateDate >= er2.ExchangeRateDate
AND er1.ExchangeRateID > er2.ExchangeRateID
)
SELECT *
FROM er
WHERE rn = 1

Perhaps you can try using a table expression to get to your TOP 1 and then JOIN to the table expression. Does that make sense? Hope this helps.

This can be solved by using one or more CTEs. This earlier SO question should have the needed building blocks :
How can you use SQL to return values for a specified date or closest date < specified date?
Note that you have to modify this to your own schema, and also filter out results that are closer but in the future.
I hope this helps, but if not enough then I'm sure I can post a more detailed answer.

If i don't misunderstand what you want to do you could use an outer apply to get the latest exchange rate.
select *
from ExpenseReportLineItem erli
outer apply (select top 1 *
from ExchangeRates as er1
where er1.Country = erli.Country and
er1.ExchangeRateDate <= erli.LineItemDate
order by er1.ExchangeRateDate desc) as er

You can use this as an correlated subquery that will give you a table with the most recent exchange values for a given date (indicated in a comment):
SELECT *
FROM er
INNER JOIN
(
SELECT CountryID, MAX(ExchangeRateDate) AS ExchangeRateDate
FROM er
WHERE ExchangeRateDate <= '9/1/2011'
-- the above is the date you will need to correlate with the main query...
GROUP BY Country
) iq
ON iq.Country = er.Country AND er.ExchangeRateDate = iq.ExchangeRateDate
So the full query should look something like this:
SELECT
iq2.ExchangeRateID,
iq2.CountryID AS Expr1,
iq2.ExchangeRateDate,
iq2.ToUSD,
erli.ExpenseReportLineItemID,
erli.ExpenseReportID,
erli.LineItemDate
FROM dbo.ExpenseReportLineItem AS erli
LEFT JOIN
(
SELECT *
FROM ExchangeRate er
INNER JOIN
(
SELECT CountryID, MAX(ExchangeRateDate) AS ExchangeRateDate
FROM ExchangeRate er
WHERE ExchangeRateDate <= erli.LineItemDate
-- the above is where the correlation occurs...
GROUP BY Country
) iq
ON iq.Country = er.Country AND er.ExchangeRateDate = iq.ExchangeRateDate
) iq2
ON er.CountryID = erli.CountryID
AND DATEADD(d, DATEDIFF(d, 0, iq2.ExchangeRateDate), 0) <= DATEADD(d, DATEDIFF(d, 0, erli.LineItemDate), 0)
WHERE (erli.ExpenseReportID = 196)

Related

Delete the records repeated by date, and keep the oldest

I have this query, and it returns the following result, I need to delete the records repeated by date, and keep the oldest, how could I do this?
select
a.EMP_ID, a.EMP_DATE,
from
EMPLOYES a
inner join
TABLE2 b on a.table2ID = b.table2ID and b.ID_TYPE = 'E'
where
a.ID = 'VJAHAJHSJHDAJHSJDH'
and year(a.DATE) = 2021
and month(a.DATE) = 1
and a.ID <> 31
order by
a.DATE;
Additionally, I would like to fill in the missing days of the month ... and put them empty if I don't have that data, can this be done?
I would appreciate if you could guide me to solve this problem
Thank you!
The other answers miss some of the requirement..
Initial step - do this once only. Make a calendar table. This will come in handy for all sorts of things over the time:
DECLARE #Year INT = '2000';
DECLARE #YearCnt INT = 50 ;
DECLARE #StartDate DATE = DATEFROMPARTS(#Year, '01','01')
DECLARE #EndDate DATE = DATEADD(DAY, -1, DATEADD(YEAR, #YearCnt, #StartDate));
;WITH Cal(n) AS
(
SELECT 0 UNION ALL SELECT n + 1 FROM Cal
WHERE n < DATEDIFF(DAY, #StartDate, #EndDate)
),
FnlDt(d, n) AS
(
SELECT DATEADD(DAY, n, #StartDate), n FROM Cal
),
FinalCte AS
(
SELECT
[D] = CONVERT(DATE,d),
[Dy] = DATEPART(DAY, d),
[Mo] = DATENAME(MONTH, d),
[Yr] = DATEPART(YEAR, d),
[DN] = DATENAME(WEEKDAY, d),
[N] = n
FROM FnlDt
)
SELECT * INTO Cal FROM finalCte
ORDER BY [Date]
OPTION (MAXRECURSION 0);
credit: mostly this site
Now we can write some simple query to stick your data (with one small addition) onto it:
--your query, minus the date bits in the WHERE, and with a ROW_NUMBER
WITH yourQuery AS(
SELECT a.emp_id, a.emp_date,
ROW_NUMBER() OVER(PARTITION BY CAST(a.emp_date AS DATE) ORDER BY a.emp_date) rn
FROM EMPLOYES a
INNER JOIN TABLE2 b on a.table2ID = b.table2ID
WHERE a.emp_id = 'VJAHAJHSJHDAJHSJDH' AND a.id <> 31 AND b.id_type = 'E'
)
--your query, left joined onto the cal table so that you get a row for every day even if there is no emp data for that day
SELECT c.d, yq.*
FROM
Cal c
LEFT JOIN yourQuery yq
ON
c.d = CAST(yq.emp_date AS DATE) AND --cut the time off
yq.rn = 1 --keep only the earliest time per day
WHERE
c.d BETWEEN '2021-01-01' AND EOMONTH('2021-01-01')
We add a rownumbering to your table, it restarts every time the date changes and counts up in order of time. We make this into a CTE (or a subquery, CTE is cleaner) then we simply left join it to the calendar table. This means that for any date you don't have data, you still have the calendar date. For any days you do have data, the rownumber rn being a condition of the join means that only the first datetime from each day is present in the results
Note: something is wonky about your question . You said you SELECT a.emp_id and your results show 'VJAHAJHSJHDAJHSJDH' is the emp id, but your where clause says a.id twice, once as a string and once as a number - this can't be right, so I've guessed at fixing it but I suspect you have translated your query into something for SO, perhaps to hide real column names.. Also your SELECT has a dangling comma that is a syntax error.
If you have translated/obscured your real query, make absolutely sure you understand any answer here when translating it back. It's very frustrating when someone is coming back and saying "hi your query doesn't work" then it turns out that they damaged it trying to translate it back to their own db, because they hid the real column names in the question..
FInally, do not use functions on table data in a where clause; it generally kills indexing. Always try and find a way of leaving table data alone. Want all of january? Do like I did, and say table.datecolumn BETWEEN firstofjan AND endofjan etc - SQLserver at least stands a chance of using an index for this, rather than calling a function on every date in the table, every time the query is run
You can use ROW_NUMBER
WITH CTE AS
(
SELECT a.EMP_ID, a.EMP_DATE,
RN = ROW_NUMBER() OVER (PARTITION BY a.EMP_ID, CAST(a.DATE as Date) ORDER BY a.DATE ASC)
from EMPLOYES a INNER JOIN TABLE2 b
on a.table2ID = b.table2ID
and b.ID_TYPE = 'E'
where a.ID = 'VJAHAJHSJHDAJHSJDH'
and year(a.DATE) = 2021
and MONTH(a.DATE) = 1
and a.ID <> 31
)
SELECT * FROM CTE
WHERE RN = 1
Try with an aggregate function MAX or MIN
create table #tmp(dt datetime, val numeric(4,2))
insert into #tmp values ('2021-01-01 10:30:35', 1)
insert into #tmp values ('2021-01-02 10:30:35', 2)
insert into #tmp values ('2021-01-02 11:30:35', 3)
insert into #tmp values ('2021-01-03 10:35:35', 4)
select * from #tmp
select tmp.*
from #tmp tmp
inner join
(select max(dt) as dt, cast(dt as date) as dt_aux from #tmp group by cast(dt as date)) compressed_rows on
tmp.dt = compressed_rows.dt
drop table #tmp
results:

Left Join without duplicate values from the left table when multiple values in right table

The Following query brings back a list of IDs and a list of times. It's bringing back multimple from the Left table because it appears multiple in the right table.
It Currently brings back this:
I want it to bring back the minimum value from the right table for each ID in the left table.
Any help appreciated!
set language british
SELECT dbo.Employee.EmployeeID, SUBSTRING(CONVERT(varchar, ClockTemp.ClockTime, 108), 1, 5) as ClockTime
FROM dbo.Employee LEFT JOIN
(
Select [ClockID]
,[EmployeeID]
,[ClockTypeID]
,[ClockDate]
,[ClockTime]
FROM dbo.Clock
WHERE dbo.Clock.ClockDate = DATEADD(DD, 0, CAST('07-11-2016' AS DATE)) AND Clock.ClockTypeID=1
) As ClockTemp
ON dbo.Employee.EmployeeID = ClockTemp.EmployeeID
ORDER BY dbo.Employee.EmployeeID,clocktemp.ClockTime
Maybe this query would work for you. You do not need to order by Clocktime as the minimum value will be brought through from the MIN aggregate.
I would also change the Clocktime field to TIME if you are using SQL 2008 so that you can avoid the substring convert.
set language british
SELECT dbo.Employee.EmployeeID, SUBSTRING(CONVERT(varchar, MIN(ClockTemp.ClockTime), 108), 1, 5) as ClockTime
FROM dbo.Employee LEFT JOIN
(
Select [ClockID]
,[EmployeeID]
,[ClockTypeID]
,[ClockDate]
,[ClockTime]
FROM dbo.Clock
WHERE dbo.Clock.ClockDate = DATEADD(DD, 0, CAST('07-11-2016' AS DATE)) AND Clock.ClockTypeID=1
) As ClockTemp
ON dbo.Employee.EmployeeID = ClockTemp.EmployeeID
GROUP BY dbo.Employee.EmployeeID
ORDER BY dbo.Employee.EmployeeID
Try that:
WITH minClockTimeCTE AS (
SELECT EmployeeID, min(ClockTime) as ClockTime
FROM ClockTemp
GROUP BY EmployeeID
)
SELECT Employee.EmployeeID, minClockTimeCTE.ClockTime
FROM Employee
LEFT OUTER JOIN minClockTimeCTE
ON Employee.EmployeeID = minClockTimeCTE.EmployeeID
You could change the left join to an outer apply top 1:
SELECT dbo.Employee.EmployeeID,
SUBSTRING(CONVERT(varchar, ClockTemp.ClockTime, 108), 1, 5) as ClockTime
FROM dbo.Employee
OUTER APPLY
(
SELECT TOP 1 [ClockID]
,[EmployeeID]
,[ClockTypeID]
,[ClockDate]
,[ClockTime]
FROM dbo.Clock
WHERE dbo.Clock.ClockDate = DATEADD(DD, 0, CAST('07-11-2016' AS DATE))
AND Clock.ClockTypeID=1
AND dbo.Employee.EmployeeID = ClockTemp.EmployeeID
ORDER BY ClockTime
) As ClockTemp
ORDER BY dbo.Employee.EmployeeID,clocktemp.ClockTime

Select first and last record each day

I have a table with an engineerID, DateTimeCreated as DateTime, JobID and AuditTypeID
I need a query shows first (engineerID, JobID with AuditTypeID 1) and last (engineerID, JobID with AuditTypeID 2) on each row of the query.
SELECT TOP (100) PERCENT
dbo.AuditTrail.EngineerId,
dbo.AuditTrail.AuditTypeId,
dbo.Engineers.Name,
dbo.Engineers.EngineerTypeCode,
dbo.AuditTrail.JobId,
CAST(dbo.AuditTrail.DateTimeCreated AS Date) AS _Date
FROM
dbo.AuditTrail
INNER JOIN
dbo.Engineers
ON dbo.AuditTrail.EngineerId = dbo.Engineers.EngineerId
WHERE
(dbo.AuditTrail.AuditTypeId = 1) AND
(dbo.Engineers.EngineerTypeCode = 'p') AND
(dbo.Engineers.EngineerTypeCode = 'p') AND
(DATEPART(mm, dbo.AuditTrail.DateTimeCreated) = 6) AND
(DATEPART(YYYY, dbo.AuditTrail.DateTimeCreated) = 2014)
group by
AuditTrail.engineerID,
JobID,
AuditTypeId,
Engineers.name,
Engineers.EngineerTypeCode,
CAST(dbo.AuditTrail.DateTimeCreated AS Date)
ORDER BY
dbo.AuditTrail.EngineerID DESC
for the first part of my query. Unfortunatly I cannot see to select the first record for each day
Any help will be greatly appreciated
First just get the data you need, including the create date. Then grouping that data by date, select the min of each day. Finally, join the two sets, selecting only the minimum of each day -- that is, the first occurrence of each day.
with
AllMonth( EngineerId, AuditTypeId, Name, EngineerTypeCode, JobId, DateTimeCreated )as(
SELECT TOP (100) PERCENT
a.EngineerId,
a.AuditTypeId,
e.Name,
e.EngineerTypeCode,
a.JobId,
a.DateTimeCreated
FROM dbo.AuditTrail a
JOIN dbo.Engineers e
ON e.EngineerId = a.EngineerId
AND e.EngineerTypeCode = a.EngineerTypeCode
WHERE
a.AuditTypeId = 1
AND a.EngineerTypeCode = 'p'
AND a.DateTimeCreated >= DateAdd( mm, DateDiff( mm, 0, GetDate()), 0)
AND a.DateTimeCreated < DateAdd( mm, DateDiff( mm, 0, GetDate()) + 1, 0)
),
FirstByDay( MinDate )as(
select Min( DateTimeCreated )
from AllMonth
group by cast( DateTimeCreated AS Date )
)
select *
from AllMonth a
join FirstByDay f
on f.MinDate = a.DateTimeCreated
ORDER BY a.EngineerID DESC;
To get the last item of each day, just add a max to FirstByDay and add to the join. Work it into one long row if you really want to.
Btw, didn't I hear a few years back that the later versions of MSSQL ignored top (100) percent? I don't work with it much these days, and my memory is...well, just...somewhere around here...

How to determine if two records are 1 year apart (using a timestamp)

I need to analyze some weblogs and determine if a user has visited once, taken a year break, and visited again. I want to add a flag to every row (Y/N) with a VisitId that meets the above criteria.
How would I go about creating this sql?
Here are the fields I have, that I think need to be used (by analyzing the timestamp of the first page of each visit):
VisitID - each visit has a unique Id (ie. 12356, 12345, 16459)
UserID - each user has one Id (ie. steve = 1, ted = 2, mark = 12345, etc...)
TimeStamp - looks like this: 2010-01-01 00:32:30.000
select VisitID, UserID, TimeStamp from page_view_t where pageNum = 1;
thanks - any help would be greatly appreciated.
You could rank every user's rows, then join the ranked row set to itself to compare adjacent rows:
;
WITH ranked AS (
SELECT
*,
rnk = ROW_NUMBER() OVER (PARTITION BY UserID ORDER BY TimeStamp)
FROM page_view_t
),
flagged AS (
SELECT
*,
IsReturnVisit = CASE
WHEN EXISTS (
SELECT *
FROM ranked
WHERE UserID = r.UserID
AND rnk = r.rnk - 1
AND TimeStamp <= DATEADD(YEAR, -1, r.TimeStamp)
)
THEN 'Y'
ELSE 'N'
END
FROM ranked r
)
SELECT
VisitID,
UserID,
TimeStamp,
IsReturnVisit
FROM flagged
Note: the above flags only return visits.
UPDATE
To flag the first visits same as return visits, the flagged CTE could be modified as follows:
…
SELECT
*,
IsFirstOrReturnVisit = CASE
WHEN p.UserID IS NULL OR r.TimeStamp >= DATEADD(YEAR, 1, p.TimeStamp)
THEN 'Y'
ELSE 'N'
END
FROM ranked r
LEFT JOIN ranked p ON r.UserID = p.UserID AND r.rnk = p.rnk + 1
…
References that might be useful:
WITH common_table_expression (Transact-SQL)
Ranking Functions (Transact-SQL)
ROW_NUMBER (Transact-SQL)
The other guy was faster but since I took time to do it and it's a completely different approach I might as well post It :D.
SELECT pv2.VisitID,
pv2.UserID,
pv2.TimeStamp,
CASE WHEN pv1.VisitID IS NOT NULL
AND pv3.VisitID IS NULL
THEN 'YES' ELSE 'NO' END AS IsReturnVisit
FROM page_view_t pv2
LEFT JOIN page_view_t pv1 ON pv1.UserID = pv2.UserID
AND pv1.VisitID <> pv2.VisitID
AND (pv1.TimeStamp <= DATEADD(YEAR, -1, pv2.TimeStamp)
OR pv2.TimeStamp <= DATEADD(YEAR, -1, pv1.TimeStamp))
AND pv1.pageNum = 1
LEFT JOIN page_view_t pv3 ON pv1.UserID = pv3.UserID
AND (pv3.TimeStamp BETWEEN pv1.TimeStamp AND pv2.TimeStamp
OR pv3.TimeStamp BETWEEN pv2.TimeStamp AND pv1.TimeStamp)
AND pv3.pageNum = 1
WHERE pv2.pageNum = 1
Assuming page_view_t table stores UserID and TimeStamp details of each visit of the user, the following query will return users who have visited taking a break of at least an year (365 days) between two consecutive visits.
select t1.UserID
from page_view_t t1
where (
select datediff(day, max(t2.[TimeStamp]), t1.[TimeStamp])
from page_view_t t2
where t2.UserID = t1.UserID and t2.[TimeStamp] < t1.[TimeStamp]
group by t2.UserID
) >= 365

How to output only one max value from this query in SQL?

Yesterday Thomas helped me a lot by providing exactly the query I wanted. And now I need a variant of it, and hopes someone can help me out.
I want it to output only one row, namely a max value - but it has to build on the algorithm in the following query:
WITH Calendar AS (SELECT CAST(#StartDate AS datetime) AS Date
UNION ALL
SELECT DATEADD(d, 1, Date) AS Expr1
FROM Calendar AS Calendar_1
WHERE (DATEADD(d, 1, Date) < #EndDate))
SELECT C.Date, C2.Country, COALESCE (SUM(R.[Amount of people per day needed]), 0) AS [Allocated testers]
FROM Calendar AS C CROSS JOIN
Country AS C2 LEFT OUTER JOIN
Requests AS R ON C.Date BETWEEN R.[Start date] AND R.[End date] AND R.CountryID = C2.CountryID
WHERE (C2.Country = #Country)
GROUP BY C.Date, C2.Country OPTION (MAXRECURSION 0)
The output from above will be like:
Date Country Allocated testers
06/01/2010 Chile 3
06/02/2010 Chile 4
06/03/2010 Chile 0
06/04/2010 Chile 0
06/05/2010 Chile 19
but what I need right now is
Allocated testers
19
that is - only one column - one row - the max value itself... (for the (via parameters (that already exists)) selected period of dates and country)
use order and limit
ORDER BY 'people needed DESC' LIMIT 1
EDITED
as LIMIT is not exist in sql
use ORDER BY and TOP
select TOP 1 .... ORDER BY 'people needed' DESC
WITH Calendar
AS (
SELECT
CAST(#StartDate AS datetime) AS Date
UNION ALL
SELECT
DATEADD(d, 1, Date) AS Expr1
FROM
Calendar AS Calendar_1
WHERE
( DATEADD(d, 1, Date) < #EndDate )
)
SELECT TOP 1 *
FROM
(
SELECT
C.Date
,C2.Country
,COALESCE(SUM(R.[Amount of people per day needed]), 0) AS [Allocated testers]
FROM
Calendar AS C
CROSS JOIN Country AS C2
LEFT OUTER JOIN Requests AS R
ON C.Date BETWEEN R.[Start date] AND R.[End date]
AND R.CountryID = C2.CountryID
WHERE
( C2.Country = #Country )
GROUP BY
C.Date
,C2.Country
OPTION
( MAXRECURSION 0 )
) lst
ORDER BY lst.[Allocated testers] DESC
Full example following the discussion in #Salil answer..
WITH Calendar AS (SELECT CAST(#StartDate AS datetime) AS Date
UNION ALL
SELECT DATEADD(d, 1, Date) AS Expr1
FROM Calendar AS Calendar_1
WHERE (DATEADD(d, 1, Date) < #EndDate))
SELECT TOP 1 C.Date, C2.Country, COALESCE (SUM(R.[Amount of people per day needed]), 0) AS [Allocated testers]
FROM Calendar AS C CROSS JOIN
Country AS C2 LEFT OUTER JOIN
Requests AS R ON C.Date BETWEEN R.[Start date] AND R.[End date] AND R.CountryID = C2.CountryID
WHERE (C2.Country = #Country)
GROUP BY C.Date, C2.Country
ORDER BY 3 DESC
OPTION (MAXRECURSION 0)
the ORDER BY 3 means order by the 3rd field in the SELECT statement.. so if you remove the first two fields, change this accordingly..