Using SELECT result in another SELECT - sql

So here is my query
SELECT
*
FROM
Score AS NewScores
WHERE
InsertedDate >= DATEADD(mm, -3, GETDATE());
SELECT
ROW_NUMBER() OVER( ORDER BY NETT) AS Rank,
Name,
FlagImg,
Nett,
Rounds
FROM (
SELECT
Members.FirstName + ' ' + Members.LastName AS Name,
CASE
WHEN MenuCountry.ImgURL IS NULL THEN
'~/images/flags/ismygolf.png'
ELSE
MenuCountry.ImgURL
END AS FlagImg,
AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett,
COUNT(Score.ScoreID) AS Rounds
FROM
Members
INNER JOIN
Score
ON Members.MemberID = Score.MemberID
LEFT OUTER JOIN MenuCountry
ON Members.Country = MenuCountry.ID
WHERE
Members.Status = 1
GROUP BY
Members.FirstName + ' ' + Members.LastName,
MenuCountry.ImgURL
) AS Dertbl
ORDER BY;
The query is to give a result set for a GridView based leaderboard and what I want is to only get the average of Scores that are less than 3 months old. I have this in 2 parts as you can see and obviously it gives an error like this.
Msg 4104, Level 16, State 1, Line 2
The multi-part identifier "NewScores.NetScore" could not be bound.
Which is because of this AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett
How do I make it so that I can use NewScores there so I'm only getting the average of the scores less than 3 months old?
EDIT: Using the answers people provided I've solved it by using a join in the correct place and here is the correct query:
SELECT ROW_NUMBER() OVER(ORDER BY NETT) AS Rank, Name, FlagImg, Nett, Rounds FROM (SELECT Members.FirstName + ' ' + Members.LastName AS Name, CASE WHEN MenuCountry.ImgURL IS NULL THEN '~/images/flags/ismygolf.png' ELSE MenuCountry.ImgURL END AS FlagImg, AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett, COUNT(NewScores.ScoreID) AS Rounds FROM Members INNER JOIN (SELECT * FROM Score WHERE InsertedDate >= DATEADD(mm, -5, GETDATE())) NewScores ON Members.MemberID = NewScores.MemberID LEFT OUTER JOIN MenuCountry ON Members.Country = MenuCountry.ID WHERE Members.Status = 1 GROUP BY Members.FirstName + ' ' + Members.LastName, MenuCountry.ImgURL) AS Dertbl ORDER BY Nett ASC

NewScores is an alias to Scores table - it looks like you can combine the queries as follows:
SELECT
ROW_NUMBER() OVER( ORDER BY NETT) AS Rank,
Name,
FlagImg,
Nett,
Rounds
FROM (
SELECT
Members.FirstName + ' ' + Members.LastName AS Name,
CASE
WHEN MenuCountry.ImgURL IS NULL THEN
'~/images/flags/ismygolf.png'
ELSE
MenuCountry.ImgURL
END AS FlagImg,
AVG(CAST(NewScores.NetScore AS DECIMAL(18, 4))) AS Nett,
COUNT(Score.ScoreID) AS Rounds
FROM
Members
INNER JOIN
Score NewScores
ON Members.MemberID = NewScores.MemberID
LEFT OUTER JOIN MenuCountry
ON Members.Country = MenuCountry.ID
WHERE
Members.Status = 1
AND NewScores.InsertedDate >= DATEADD(mm, -3, GETDATE())
GROUP BY
Members.FirstName + ' ' + Members.LastName,
MenuCountry.ImgURL
) AS Dertbl
ORDER BY;

What you are looking for is a query with WITH clause, if your dbms supports it. Then
WITH NewScores AS (
SELECT *
FROM Score
WHERE InsertedDate >= DATEADD(mm, -3, GETDATE())
)
SELECT
<and the rest of your query>
;
Note that there is no ; in the first half. HTH.

You are missing table NewScores, so it can't be found. Just join this table.
If you really want to avoid joining it directly you can replace NewScores.NetScore with SELECT NetScore FROM NewScores WHERE {conditions on which they should be matched}

Related

SQL select distinct from a concatenated column

This query almost does what I want
SELECT staging.dbo.ITEM_CODES.ITEM_CODE, MAX(dbo.OC_VDAT_AUX.UDL40) AS SAMPLEDATE,
CONCAT(RTRIM(dbo.OC_VDATA.UDL1), RTRIM(dbo.OC_VDATA.UDL6)) as LinkID
FROM dbo.OC_VDATA
INNER JOIN dbo.OC_VDAT_AUX ON dbo.OC_VDATA.PARTNO = dbo.OC_VDAT_AUX.PARTNOAUX AND dbo.OC_VDATA.DATETIME = dbo.OC_VDAT_AUX.DATETIMEAUX
INNER JOIN stagingPLM.dbo.ITEM_CODES ON LEFT(dbo.OC_VDATA.PARTNO, 12) = staging.dbo.ITEM_CODES.SPEC_NO
AND LEFT(dbo.OC_VDAT_AUX.PARTNOAUX, 12) = stagingPLM.dbo.ITEM_CODES.SPEC_NO
INNER JOIN stagingPLM.dbo.PLANTS ON dbo.OC_VDATA.UDL1 = staging.dbo.PLANTS.PLANT_CODE
WHERE (CONVERT(DATETIME, dbo.OC_VDAT_AUX.UDL40) > DATEADD(day, - 30, GETDATE()))
GROUP BY CONCAT(RTRIM(dbo.OC_VDATA.UDL1), RTRIM(dbo.OC_VDATA.UDL6)),staging.dbo.ITEM_CODES.ITEM_CODE
Sample Table generated by query:
The end result that I am trying to achieve is the latest ITEM_CODE per unique LinkID Note the first and last rows in the table. The last row should not be pulled by the query.
How do I modify this query to make that happen?
I have tried various placements for DISTINCT and sub queries in the select and where statements.
I would do in your case with ROW_NUMBER window function and CTE.
Solution can be like this:
WITH FilterCTE AS
(
SELECT staging.dbo.ITEM_CODES.ITEM_CODE, MAX(dbo.OC_VDAT_AUX.UDL40) AS SAMPLEDATE,
CONCAT(RTRIM(dbo.OC_VDATA.UDL1), RTRIM(dbo.OC_VDATA.UDL6)) AS LinkID,
ROW_NUMBER() OVER (PARTITION BY CONCAT(RTRIM(dbo.OC_VDATA.UDL1), RTRIM(dbo.OC_VDATA.UDL6)) ORDER BY MAX(dbo.OC_VDAT_AUX.UDL40)) AS RowNumber
FROM dbo.OC_VDATA
INNER JOIN dbo.OC_VDAT_AUX ON dbo.OC_VDATA.PARTNO = dbo.OC_VDAT_AUX.PARTNOAUX AND dbo.OC_VDATA.DATETIME = dbo.OC_VDAT_AUX.DATETIMEAUX
INNER JOIN stagingPLM.dbo.ITEM_CODES ON LEFT(dbo.OC_VDATA.PARTNO, 12) = staging.dbo.ITEM_CODES.SPEC_NO
AND LEFT(dbo.OC_VDAT_AUX.PARTNOAUX, 12) = stagingPLM.dbo.ITEM_CODES.SPEC_NO
INNER JOIN stagingPLM.dbo.PLANTS ON dbo.OC_VDATA.UDL1 = staging.dbo.PLANTS.PLANT_CODE
WHERE (CONVERT(DATETIME, dbo.OC_VDAT_AUX.UDL40) > DATEADD(day, - 30, GETDATE()))
GROUP BY CONCAT(RTRIM(dbo.OC_VDATA.UDL1), RTRIM(dbo.OC_VDATA.UDL6)),staging.dbo.ITEM_CODES.ITEM_CODE
)
SELECT *
FROM FilterCTE
WHERE RowNumber = 1

oracle sql Find max avg of three attempts

Hey need help with finding maximum average weight of three columnes (attempt1, attempt2 and attempt3 and showing which exercise they belong to and what date they occurred.
Atm, the results of the below query outputs, all exercises, all dates they occurred on and all the avg weights. I want it to select the max avg weight, the exercise it belongs to and date it occurred. Attached is the result of the query and ive highlighted what I want to be the output.
Result of query
SELECT e.exercise_description
, occ.occ_date, (COALESCE(OE.ATTEMPT1, 0)
+ COALESCE(OE.ATTEMPT2, 0)
+ COALESCE(OE.ATTEMPT3, 0))
/(3 -(COALESCE(OE.ATTEMPT1 - OE.ATTEMPT1, 1)
+ COALESCE(OE.ATTEMPT2 - OE.ATTEMPT2, 1)
+ COALESCE(OE.ATTEMPT3 - OE.ATTEMPT3, 1))
) AS row_avg
FROM EXERCISE E INNER JOIN Occurrence_Exercise OE
ON E.EXERCISEID=OE.EXERCISEID
INNER JOIN OCCURRENCE OCC ON OE.OCCURRENCEID=OCC.OCCURRENCEID;
You can just use window row_number() function to get the biggest avg one
SELECT e.exercise_description
, occ.occ_date
FROM
(SELECT oe.exerciseid
, oe.occurrenceid
, ROW_NUMBER() OVER(ORDER BY (COALESCE(oe.attempt1, 0) + COALESCE(oe.attempt2, 0)+ COALESCE(oe.attempt3, 0))
/ (NVL2(oe.attempt1, 1, 0) + NVL2(oe.attempt2, 1, 0) + NVL2(oe.attempt3, 1, 0))
) AS rn
FROM Occurrence_Exercise oe
) srt
INNER JOIN exercise e
ON srt.exerciseid=e.exerciseid
INNER JOIN occurrence occ
ON srt.occurrenceid=occ.occurrenceid
WHERE srt.rn = 1

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 Distinct date and Count

My sample table structure is:
TestDate
----------------------
2013-03-25 14:26:40.830
2013-03-20 13:37:39.763
2012-09-10 14:55:55.667
2013-03-20 13:33:20.480
And my query is :
SELECT DISTINCT
REPLACE(RIGHT(CONVERT(VARCHAR(20), TestDate, 106), 8), ' ', '-') AS TT
,(SELECT COUNT(*)
FROM Test bp
WHERE
CONVERT(VARCHAR(20), p.TestDate, 6) = CONVERT(VARCHAR(20), bp.TestDate, 6)) AS Posts
FROM Test p
I got a result:
TT Posts
Mar-2013 1
Mar-2013 2
Sep-2012 1
But I want a result:
TT Posts
Mar-2013 3
Sep-2012 1
But I am unable to find my mistake in my query. Thanks.
Are you trying to select the rows and count for each month?
If so - try something like this:
SELECT
YEAR(Testdate), MONTH(Testdate),
COUNT(*) totalPost
FROM
tableName
GROUP BY
YEAR(Testdate), MONTH(Testdate)
Update: if you insist on formatting that inside SQL Server (which I think is the wrong place to do this...) - then use something like this:
SELECT DISTINCT
SUBSTRING(DATENAME(MONTH, TestDate), 1, 3) + '-' + CAST(YEAR(TestDate) AS VARCHAR(4)),
YEAR(Testdate), MONTH(Testdate),
TotalPosts = COUNT(*)
FROM
tableName
GROUP BY
SUBSTRING(DATENAME(MONTH, TestDate), 1, 3) + '-' + CAST(YEAR(TestDate) AS VARCHAR(4)),
YEAR(Testdate), MONTH(Testdate)
ORDER BY
YEAR(Testdate), MONTH(Testdate)
You need to cast DATETIME into DATE first.
SELECT CAST(TestDate AS DATE) DATE_ONLY,
COUNT(*) totalPost
FROM tableName
GROUP BY CAST(TestDate AS DATE)

Tough T-SQL To Left Join?

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)