Select all from max date - sql

Good morning,
I am writing a SQL query for the latest metal prices with the latest date they were put into the database. Example table below:
ID Date Created
1 01/01/01 01:01
2 01/01/01 01:02
3 01/01/01 01:03
4 01/01/01 01:04
1 02/01/01 01:01
2 02/01/01 01:02
So from this I want the following result:
ID Date Created
1 02/01/01 01:01
2 02/01/01 01:02
When I run the below query it is just giving me the last one entered into the date base so from the above example it would be ID 2 DateCreated 02/01/01 01:02. The query I am using is below:
SELECT mp.MetalSourceID, ROUND(mp.PriceInPounds,2),
mp.UnitPrice, mp.HighUnitPrice, mp.PreviousUnitPrice,
mp.PreviousHighUnitPrice, ms.MetalSourceName,
ms.UnitBasis, cu.Currency
FROM tblMetalPrice AS mp
INNER JOIN tblMetalSource AS ms
ON tblMetalPrice.MetalSourceID = tblMetalSource.MetalSourceID
INNER JOIN tblCurrency AS cu
ON tblMetalSource.CurrencyID = tblCurrency.CurrencyID
WHERE DateCreated = (SELECT MAX (DateCreated) FROM tblMetalPrice)
GROUP BY mp.MetalSourceID;
Could anyone please help its driving me crazy not knowing and my brain is dead this friday morning.
Thanks

Use a correlated subquery for the where clause:
WHERE DateCreated = (SELECT MAX(DateCreated) FROM tblMetalPrice mp2 WHERE mp2.id = mp.id)

You can join on a subquery, and I don't think you'll need the group by, or indeed the where clause (because that's handled by the join).
SELECT mp.MetalSourceID,
ROUND(mp.PriceInPounds,2),
mp.UnitPrice,
mp.HighUnitPrice,
mp.PreviousUnitPrice,
mp.PreviousHighUnitPrice,
ms.MetalSourceName,
ms.UnitBasis,
cu.Currency
FROM tblMetalPrice AS mp
INNER JOIN tblMetalSource AS ms
ON tblMetalPrice.MetalSourceID = tblMetalSource.MetalSourceID
INNER JOIN tblCurrency AS cu
ON tblMetalSource.CurrencyID = tblCurrency.CurrencyID
INNER JOIN (SELECT ID,MAX(DateCreated) AS maxdate FROM tblMetalPrice GROUP BY ID) AS md
ON md.ID = mp.ID
AND md.maxdate = mp.DateCreated

with maxDates as
(select max(datecreated) maxd, ids grp , count(1) members from s_tableA group by ids having count(1) > 1)
select ids, datecreated from s_tableA,maxDates
where maxd = datecreated and ids = grp;
this query will give your desired result. Correlated sub queries tend to consume lot of processing time, because for each row of the outer query it has to process all the rows in the inner query.

Related

Count current job with a partition

The next code makes a join between users and payment, to get the last payment.
The query should work if the payment table did not contain duplicated rows with the same max_date as the following one.
Something to notice, is that the row is not completely duplicated, sometimes contains little changes. But we do not care if we select the 'right' one, we only need it to be one, no matter which one of those is.
user_ID | Payment | date | product | credit_card
1 300 1/1/2020 A No
1 300 1/1/2020 Null | No
1 300 1/1/2021 A Yes
1 300 1/1/2021 Null | Yes
This causes the second inner join to duplicate rows because it makes a match twice with the maxDate which is 1/1/2021
SELECT a.*, c.*
FROM users a
INNER JOIN payments c
ON a.id = c.user_ID
INNER JOIN
(
SELECT user_ID, MAX(date) maxDate
FROM payments
GROUP BY user_ID
) b ON c.user_ID = b.user_ID AND
c.date = b.maxDate
I'm looking for a way to select only the first match of the maxDate. Any clue is welcome, thank in advance for any help.
You should be using window functions for this. That would be:
SELECT u.*, p.*
FROM users u JOIN
(SELECT p.*,
ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY p.date DESC) as seqnum
FROM payments p
) p
ON p.user_ID = u.id AND p.seqnum = 1;
This returns one row, but which row is arbitrary.
Note the use of meaningful table aliases in the query -- u for users and p for `payments. Don't use meaningless letters. They just make the query hard to read -- and to maintain.

How to join the results of two queries in just one table grouped by YEAR and MONTH?

I have two tables,materials_students and components_students. Both of them has afinished_at column. material_student has a component_student_id column.
I need to count the number of components_students and materials_students (Where finished_at id is not NULL), extract month and year from finished_at, group the result by month and year and plot it in just one table, like this:
| Materials | Components | Month | Year
---------------------------------------------
| 45 3 1 2019
| 37 6 2 2019
| 63 8 3 2019
I know how to do this for one table only, but dont know how to join the results in just one table.
Find below how I did for one table:
FROM materials_students
LEFT JOIN students ON materials_students.student_id = students.id
LEFT JOIN company_profiles ON students.company_profile_id = company_profiles.id
LEFT JOIN companies ON company_profiles.company_id = companies.id
WHERE materials_students.finished_at IS NOT NULL
GROUP BY YEAR, MONTH
ORDER BY YEAR, MONTH
Thanks!
The best is to assemble a subquery for each case, then join them.
select
ISNULL(M.yy, C.yy) [yy],
ISNULL(M.mm, C.mm) [mm],
ISNULL(number_material_students, 0) [number_material_students],
ISNULL(number_components_students, 0) [number_component_students]
from (
SELECT
year(materials_students.finished_at) yy,
month(materials_students.finished_at) mm,
count(*) number_material_students
FROM materials_students
LEFT JOIN students ON materials_students.student_id = students.id
LEFT JOIN company_profiles ON students.company_profile_id = company_profiles.id
LEFT JOIN companies ON company_profiles.company_id = companies.id
WHERE materials_students.finished_at IS NOT NULL
GROUP BY year(materials_students.finished_at), month(materials_students.finished_at)
) M
full outer join (
SELECT
year(components_students.finished_at) yy,
month(components_students.finished_at) mm,
count(*) number_material_students
FROM components_students
LEFT JOIN students ON components_students.student_id = students.id
LEFT JOIN company_profiles ON students.company_profile_id = company_profiles.id
LEFT JOIN companies ON company_profiles.company_id = companies.id
WHERE components_students.finished_at IS NOT NULL
GROUP BY year(materials_students.finished_at), month(materials_students.finished_at)
) C
ON C.yy = M.yy AND C.mm = M.mm
ORDER BY 1, 2
I had to make a FULL OUTER JOIN between the subqueries, because there may have been year/months that appear only on materials, but not on components, and vice-versa.
To retrieve the year I use the ISNULL() function, so in case year is not filled from the materials subquery, I use it from the components subquery. Similar reasoning applies to all other resulting columns.

How to find max value in SQL Server 2014?

I have a table named StatementSummary.
SELECT *
FROM StatementSummary
WHERE AccountID = 1234
Results
StatementId StatementDate AccountId AmountDue
-------------------------------------------------
100 2017-10-16 1234 600
99 2017-09-16 1234 500
98 2017-08-16 1234 400
I have another table that has a list of Accounts. I am trying to give results that show the last AmountDue for each account
My code:
SELECT
AccountID,
(SELECT MAX(StatementDate)
FROM StatementSummary
GROUP BY AccountID) LastStatementDate,
AmountDue
FROM
Accounts A
INNER JOIN
StatementSummary S ON A.AccountId = S.AccountId
Basically, I want to show all the details of the last statement for every AccountId.
You can use the SQL Server Windowing functions in cases like this.
SELECT DISTINCT
a.AccountId,
FIRST_VALUE(s.StatementDate) OVER (PARTITION BY s.AccountId ORDER BY s.StatementDate DESC) As LastStatementDate,
FIRST_VALUE(s.AmountDue) OVER (PARTITION BY s.AccountId ORDER BY s.StatementDate DESC) As LastAmountDue
FROM Accounts a
INNER JOIN StatementSummary s
ON a.AccountId = s.AccountId
Basically what happens is the OVER clause creates partitons in your data, in this case, by the account number (these partitions are the windows). We then tell SQL Server to sort the data within each partition by the statement date in descending order, so the last statement will be at the top of the partition, and then the FIRST_VALUE function is used to just grab the first row.
Finally, since you perform this operation for every account/statement combo between the two tables, you need the DISTINCT to say you just want one copy of each row for each account.
There are quite a bit of useful things you can do with the windowing functions in SQL Server. This article gives a good introduction to them: https://www.red-gate.com/simple-talk/sql/learn-sql-server/window-functions-in-sql-server/
Derived Table over row numberand left join - to display all accounts regardless if there is a statement
select *
from
(select row_number() over (partition by accountid order by statementdate desc) rn,
accountid, statementdate,amount
from statementtable
) l
left outer join accountstable a
on l.accountid = a.accountid
And l.rn = 1
That's sounds like a job for me sings lateral join aka cross apply in T-SQL.
SELECT a.*, last_ss.*
FROM Accounts A
cross apply (
select top 1 *
from StatementSummary S ON A.AccountId = S.AccountId
order by StatementDate desc
) last_ss
Alternatively you can use CTE to get last date for account:
; with l as (
select accountid, max(StatementDate)
from StatementSummary
group by accountid
)
select ...
from Accounts a
inner join l on l.accountid = a.accountid
inner join StatementSummary ss on ss.accountid = a.accountid
and l.StatementDate = ss.StatementDate

Last two row from multiple records assistance

I’m having a little problem that I’m not sure how to get a round and hoping someone here can assist. What I need to do is run a select on multiple records and retrieve the last two records of p.CustID. When enter in one p.CustID the code works fine however I need to remove the where clause and I need it to retrieve the last two records for each p.CustID (about 14,000 records in total) When I remove the where clause it only returns two records in total which are top two records in my from statement [DB_User].[dbo].[P1ASellers]. I tried using this in a CTE but still cannot get this to return
Code I’m using below:
SELECT TOP (2)
cbc.StorePartnerCustConfigID,
p.CustID,
cbc.ConfigID,
cbc.EffectiveDate,
ROW_NUMBER() OVER (ORDER BY cbc.StorePartnerID DESC) AS RowNum
FROM [DB_User].[dbo].[P1ASellers] p
INNER JOIN [ACA].dbo.tblConfig_StorePartnerConfig BP
ON BP.EntityID=CAST(p.CustID AS VARCHAR)
INNER JOIN [ACA].dbo.tblConfig_StorePartner CBP
ON CBP.StorePartnerID=BP.StorePartnerID
INNER JOIN [ACA].dbo.tblConfig_StorePartnerCustConfig CBC
ON CBP.StorePartnerID=CBC.StorePartnerID
AND cbc.ProcessConfigID IN (1,2,3,4)
INNER JOIN [ACA].dbo.tblConfig_StorePartnerCustConfig CBC2
ON CBC.StorePartnerID=CBC2.StorePartnerID
AND cbc2.ConfigID IN (1,2,3,4) where p.CustID=55555 <-need to remove the
where
ORDER BY cbc.StorePartnerID DESC
The results from the query
StorePartnerCustConfigID CustID ConfigID EffectiveDate RowNum
15031 55555 4 2015-06-25 1
15032 55555 1 2015-06-25 2
What I actually get after I remove the where clause:
StorePartnerCustConfigID CustID ConfigID EffectiveDate RowNum
68995 89566 2 2011-03-02 1
68996 89566 1 2011-03-02 2
what I expect after I remove the where clause:
StorePartnerCustConfigID CustID ConfigID EffectiveDate RowNum
15031 55555 4 2015-06-25 1
15032 55555 1 2015-06-25 2
64584 65486 2 2013-04-16 1
64585 65486 1 2013-04-16 2
So on and so on.......
Any input greatly appreciated, thanks!!
I think you are looking for top 2 records for each customer which you can get as below:
SELECT TOP (2) with ties
cbc.StorePartnerCustConfigID,
p.CustID,
cbc.ConfigID,
cbc.EffectiveDate,
ROW_NUMBER() OVER (ORDER BY cbc.StorePartnerID DESC) AS RowNum
FROM [DB_User].[dbo].[P1ASellers] p
INNER JOIN [ACA].dbo.tblConfig_StorePartnerConfig BP
ON BP.EntityID=CAST(p.CustID AS VARCHAR)
INNER JOIN [ACA].dbo.tblConfig_StorePartner CBP
ON CBP.StorePartnerID=BP.StorePartnerID
INNER JOIN [ACA].dbo.tblConfig_StorePartnerCustConfig CBC
ON CBP.StorePartnerID=CBC.StorePartnerID
AND cbc.ProcessConfigID IN (1,2,3,4)
INNER JOIN [ACA].dbo.tblConfig_StorePartnerCustConfig CBC2
ON CBC.StorePartnerID=CBC2.StorePartnerID
AND cbc2.ConfigID IN (1,2,3,4)
ORDER (row_number() over(partition by p.CustId Order BY cbc.StorePartnerID DESC-1)/2 +1 )

3 joins and where clause together

I have 3 tables
bl_main (bl_id UNIQUE, bl_area)
bl_details (bl_id UNIQUE, name)
bl_data(bl_id, month, paper_tons, bottles_tons)
bl_id is not unique in the last table. There will be multiple rows of same bl_id.
I am trying to retrieve data in the following way
bl_id | name | bl_area | sum(paper_tons) | sum (bottles_tons) | paper_tons | bottles_tons
sum(paper_tons) should return the sum of all the paper tons for the same bl_id like Jan to December.
Using the below query i am able to retrieve all the data correctly except in the result, there are multiple occurances of bl_ids(From bl_data table).
SELECT bl_main.bl_id,name,bl_area,sums.SummedPaper, sums.SummedBottles,paper_tons,bottles_tons
FROM bl_main
JOIN bl_details ON
bl_main.bl_id= bl_details.bl_id
left outer JOIN bl_data ON
bl_data.bl_id= bl_main.bl_id
left outer JOIN (
SELECT bl_id, SUM(Paper_tons) As SummedPaper, SUM(bottle_tons) As SummedBottles
FROM bl_data
GROUP by bl_id) sums ON sums.bl_id = bl_main.bl_id
I wanto retrieve only the unique values of bl_ids without repetition and it should contain the bl_id which has the max month and not all the months for the same bl_id.
For ex:
INCORRECT
**0601** University Hall 75.76 17051 1356 4040 1154 **11**
**0601** University Hall 75.76 17051 1356 9190 101 **12**
**0605** UIC Student 22.86 3331 14799 0 356 **8**
CORRECT
**0601** University Hall 75.76 17051 1356 9190 101 **12**
**0605** UIC Student 22.86 3331 14799 0 356 **8**
I know I can get the max value using
WHERE Month = (SELECT MAX(Month)
but where exactlt should i add this in the query and should i change the join definition.
Any help is highly appreciated as i am new to sql. Thanks in advance.
You have two tables that probably should be combined into one (bl_main and bl_details). But putting that aside, what you need is a self-join subquery to select the row with the max month. Something like the following (untested):
SELECT bl_main.bl_id, bl_details.name, bl_main.bl_area, sums.sum_paper_tons,
sums.sum_bottles_tons, maxmonth.paper_tons, maxmonth.bottles_tons
FROM bl_main
INNER JOIN bl_details ON bl_main.bl_id = bl_details.bl_id
LEFT OUTER JOIN (SELECT bl_id, SUM(paper_tons) AS sum_paper_tons,
SUM(bottles_tons) AS sum_bottles_tons
FROM bl_data
GROUP BY bl_id) sums ON bl_main.bl_id = sums.bl_id
LEFT OUTER JOIN (SELECT bl_id, paper_tons, bottles_tons
FROM bl_data data2
INNER JOIN (SELECT bl_id, MAX(month) AS max_month
FROM bl_data
GROUP BY bl_id) m
ON m.bl_id = data2.bl_id
AND m.max_month = data2.month) maxmonth
ON bl_main.bl_id = maxmonth.bl_id
You can join the table containing the month against itself, using a subquery of the form:
Select *
From mytable m
Inner Join (Select max(Month) as Month, myId
From mytable
Group By myId) mnth
On mnth.myId = m.myId and mnth.Month = m.Month
Your JOIN clause
left outer JOIN bl_data ON
bl_data.bl_id= bl_main.bl_id
does not specify which month to select for the data you are displaying with paper_tons and bottles_tons.
You could update that JOIN to only contain the max month, and this should limit the entries, like so:
left outer JOIN (SELECT bl_id, MAX(Month) as Month from bl_data GROUP BY bl_id) as Month
ON Month.bl_id = bl_main.bl_id
left outer JOIN bl_data ON
bl_data.bl_id = bl_main.bl_id AND bl_data.Month = Month.bl_Month
I think this query is what you are looking for
SELECT bl_main.bl_id,name, bl_area, sums.SummedPaper, sums.SummedBottles, paper_tons, bottles_tons
FROM bl_main
JOIN bl_details ON bl_main.bl_id= bl_details.bl_id
left outer JOIN bl_data ON bl_data.bl_id= bl_main.bl_id
left outer JOIN
(
SELECT bl_id, month, SUM(Paper_tons) As SummedPaper, SUM(bottle_tons) As SummedBottles
FROM bl_data
WHERE month in
(SELECT MAX(month) FROM bl_data GROUP BY bl_id)
GROUP BY bl_id, month
) sums ON sums.bl_id = bl_main.bl_id
I wanted to just add a comment to the answer lc gave, but I don't have 50 reputation points yet. It is a link to an article that I believe explains this question and adds the why the solution that lc gave is correct.
http://www.sqlteam.com/article/how-to-use-group-by-with-distinct-aggregates-and-derived-tables