Find MAX with JOIN where Field also shows up in another Table - sql

I have 3 tables: Master, Paper and iCodes. For a certain set of Master.Ref's, I need to find Max(Paper.Date), where the Paper.Code is also in the iCodes table (i.e., Paper.Code is a type of iCode). Master is joined to Paper by the File field.
EDIT:
I only need the Max(Paper.Date) its corresponding Code; I do not need all of the Codes.
I wrote the following but it is very slow. I have a few hundred ref #'s to look for. What is a better way to do this?
SELECT Master.Ref,
Paper.Code,
mp.MaxDate
FROM ( SELECT p.File ,
MAX(p.Date) AS MaxDate ,
FROM Paper AS p
LEFT JOIN Master AS m ON p.File = m.File
WHERE m.Ref IN ('ref1', 'ref2', 'ref3', 'ref4', 'ref5', 'ref6'... )
AND p.Code IN ( SELECT DISTINCT i.iCode
FROM iCodes AS i
)
GROUP BY p.File
) AS mp
LEFT JOIN Master ON mp.File = Master.File
LEFT JOIN Paper ON Master.File = Paper.File
AND mp.MaxDate = Paper.Date
WHERE Paper.Code IN ( SELECT DISTINCT iCodes.iCode
FROM iCodes
)

Does this do what you want?
SELECT m.Ref, p.Code, max(p.date)
FROM Master m LEFT JOIN
Paper
ON m.File = p.File
WHERE p.Code IN (SELECT DISTINCT iCodes.iCode FROM iCodes) and
m.Ref IN ('ref1','ref2','ref3','ref4','ref5','ref6'...)
GROUP BY m.Ref, p.Code;
EDIT:
To get the code on the max date, then use window functions:
select ref, code, date
from (SELECT m.Ref, p.Code, p.date
row_number() over (partition by m.Ref order by p.date desc) as seqnum
FROM Master m LEFT JOIN
Paper
ON m.File = p.File
WHERE p.Code IN (SELECT DISTINCT iCodes.iCode FROM iCodes) and
m.Ref IN ('ref1','ref2','ref3','ref4','ref5','ref6'...)
) mp
where seqnum = 1;
The function row_number() assigns a sequential number starting at 1 to a group of rows. The groups are defined by the partition by clause, so in this case everything with the same m.Ref value would be in a single group. Within the group, rows are assigned the number based on the order by clause. So, the one with the biggest date gets the value of 1. That is the row you want.

Related

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

Oracle SQL STATS_MODE with two tables

I have the tables listed in the image. I need to display the institution and loan type of the most used financing plan in the sale_financing table. I have tried using the stats_mode function but I have not been able to get it to work. I am supposed to only display the most used financing plan and I keep getting 3 showing up. Here is an image of the tables.
My image may not work so here are the tables:
financing_plan
id
institution
loan_type
sale_financings
id
plan_id ------> foreign key linking to - financing_plan.id
I have tried several different ways in the Query Builder and I cannot get it to work.
Here is one :
SELECT
financing_plans.institution,
financing_plans.loan_type,
STATS_MODE(sale_financings.plan_id) AS stats_mode_plan_id
FROM
financing_plans
INNER JOIN sale_financings ON financing_plans.id =
sale_financings.plan_id
GROUP BY
financing_plans.institution,
financing_plans.loan_type
Another:
SELECT
financing_plans.institution,
financing_plans.loan_type,
STATS_MODE(sale_financings.plan_id) AS stats_mode_plan_id
FROM
financing_plans
INNER JOIN sale_financings ON financing_plans.id =
sale_financings.plan_id
GROUP BY
financing_plans.institution,
financing_plans.loan_type
HAVING
STATS_MODE(sale_financings.plan_id) = sale_financings.plan_id
Count the use of each plan_id, then rank these (using dense_rank()) by the count (descending order) allows "top" and "equal top" to be shown.
select
fp.institution, fp.loan_type, s.plan_count
from financing_plan fp
inner join (
select plan_id, plan_count, dense_rank() over(order by plan_count DESC) as rnk
from (
select plan_id, count(id) plan_count
from sale_financings
Group by plan_id
)
) s on fp.id = s.plan_id and s.rnk = 1
order by
fp.institution, fp.loan_type
;

How to filter data out, when using GROUP in SQL

I got this SQL-statement:
SELECT t.*
FROM (SELECT *
FROM accelerator_clinic_patient_status
ORDER BY status_id DESC) t
INNER JOIN accelerator_clinic_campaign_patient_signup signup
ON (signup.patient_id = t.patient_id)
INNER JOIN accelerator_clinic_campaign campaign
ON (signup.campaign_id = campaign.campaign_id
AND campaign.clinic_user_id = 4978)
GROUP BY t.patient_id
The code finds out the latest status in the table "accelerator_clinic_patient_status"
It contains columns patient_id which is a foregin-key, each patient_id can contain multiple status's.
But what I am interested in, is getting a list of patient id whose LATEST status is "Booked" -- I want to filter out Not Booked in the list, when doing the select-statement (for example in the posted image, the data should not be shown in the query, because the latest status is Not Booked). The code right now returns a list with the latest status.
Any idea how to do this?
Try This,
Please Improve the following code syntactically,
select *,Rank() over (partiton by X.Patient_Id order by X.date_added desc) as Rankk from (SELECT t.*
FROM (SELECT *
FROM accelerator_clinic_patient_status
ORDER BY status_id DESC) t
INNER JOIN accelerator_clinic_campaign_patient_signup signup
ON (signup.patient_id = t.patient_id)
INNER JOIN accelerator_clinic_campaign campaign
ON (signup.campaign_id = campaign.campaign_id
AND campaign.clinic_user_id = 4978)
GROUP BY t.patient_id) as X where Rankk=1 and Status = 'Booked'
In Mysql ,Something like this,
SET #rank=0;
SELECT #rank:=#rank+1,*
FROM (accelerator_clinic_patient_status
ORDER BY status_id DESC) t
INNER JOIN accelerator_clinic_campaign_patient_signup signup
ON (signup.patient_id = t.patient_id)
INNER JOIN accelerator_clinic_campaign campaign
ON (signup.campaign_id = campaign.campaign_id
AND campaign.clinic_user_id = 4978)
GROUP BY t.patient_id) X
Group By Patient_Id Order By date_added Desc

Joining two tables on SQL Server

I have two SQL Server tables: ORDR (orders) and RDR1 (order's items). I'm trying to create a report which shows:
DocEntry, CardName, DocDueDate: info about the order
pTot: total amount of items in the order
ItemCode: item's code (any of them, only one is needed)
Dscription: item's name
My last attempt was:
SELECT
dbo.ORDR.DocEntry, dbo.ORDR.CardName, dbo.ORDR.DocDueDate,
SUM(dbo.RDR1.Quantity) AS pTot,
dbo.RDR1.ItemCode,
dbo.RDR1.Dscription
FROM
dbo.ORDR
INNER JOIN
dbo.RDR1 ON dbo.ORDR.DocEntry = dbo.RDR1.DocEntry
GROUP BY
dbo.ORDR.DocEntry, dbo.ORDR.CardName, dbo.ORDR.DocDueDate,
dbo.RDR1.ItemCode, dbo.RDR1.Dscription
Items' code/name in one order are very similar so I need only the first RDR1's record associated to that order
I have 2 problems:
I'm getting one row for each RDR1 record
pTot is not summing the amount of items
Can you show me how to join these tables properly?
You could use ROW_NUMBER to get the first RDR1 item for each ORDR and SUM OVER to get the total amount of items.
SELECT
o.DocEntry,
o.CardName,
o.DocDueDate,
r.pTot,
r.ItemCode,
r.Dscription
FROM dbo.ORDR o
INNER JOIN (
SELECT *,
rn = ROW_NUMBER() OVER(PARTITION BY DocEntry ORDER BY ItemCode),
pTot = SUM(Quantity) OVER(PARTITION BY DocEntry)
FROM dbo.RDR1
) r
ON r.DocEntry = o.DocEntry
WHERE r.rn = 1
Additionally, you might want to use meaningful table aliases to improve readability.
Here is my proposed solution.
SELECT
[rowno] = ROW_NUMBER() OVER(PARTITION BY DocEntry ORDER BY ItemCode),
O.DocEntry,
O.CardName,
O.DocDueDate,
SUM(Quantity) AS pTot,
O.ItemCode,
O.Dscription
INTO #TEMP_ORDER
FROM dbo.ORDR O
INNER JOIN dbo.RDR1 R
ON O.DocEntry = dbo.RDR1.DocEntry
GROUP BY O.DocEntry, O.CardName, O.DocDueDate, R.ItemCode, R.Dscription
SELECT
DocEntry,
CardName,
DocDueDate,
pTot,
ItemCode,
Dscription
FROM #TEMP_ORDER
WHERE roWno = 1
DROP TABLE #TEMP_ORDER

i want to modify this SQL statement to return only distinct rows of a column

select
picks.`fbid`,
picks.`time`,
categories.`name` as cname,
options.`name` as oname,
users.`name`
from
picks
left join categories
on (categories.`id` = picks.`cid`)
left join options
on (options.`id` = picks.oid)
left join users
on (users.fbid = picks.`fbid`)
order by
time desc
that query returns a result that like:
my question is.... I would like to modify the query to select only DISTINCT fbid's. (perhaps the first row only sorted by time)
can someone help with this?
select
p2.fbid,
p2.time,
c.`name` as cname,
o.`name` as oname,
u.`name`
from
( select p1.fbid,
min( p1.time ) FirstTimePerID
from picks p1
group by p1.fbid ) as FirstPerID
JOIN Picks p2
on FirstPerID.fbid = p2.fbid
AND FirstPerID.FirstTimePerID = p2.time
LEFT JOIN Categories c
on p2.cid = c.id
LEFT JOIN Options o
on p2.oid = o.id
LEFT JOIN Users u
on p2.fbid = u.fbid
order by
time desc
I don't know why you originally had LEFT JOINs, as it appears that all picks must be associated with a valid category, option and user... I would then remove the left, and change them to INNER joins instead.
The first inner query grabs for each fbid, the FIRST entry time which will result in a single entity for the FBID. From that, it re-joins to the picks table for the same ID and timeslot... then continues for the rest of the category, options, users join criteria of that single entry.
2 options, you could write a group by clause.
Or you could write a nested query joined back to itself to get pertinent info.
Nested aliased table:
SELECT
n.fBids
FROM
MyTable t
INNER JOIN
(SELECT DISTINCT fBids
FROM MyTable) n
ON n.ID = t.ID
Or group by option
SELECT fBId from MyTable
GROUP BY fBID
select picks.`fbid`, picks.`time`, categories.`name` as cname,
options.`name` as oname, users.`name` from picks left join categories
on (categories.`id` = picks.`cid`) left join options on (options.`id` = picks.oid)
left join users on (users.fbid = picks.`fbid`)
order by time desc GROUP BY picks.`fbid`
select
picks.fbid,
MIN(picks.time) as first_time,
MAX(picks.time) as last_time
from
picks
group by
picks.fbid
order by
MIN(picks.time) desc
However, if you want only distinct fbid's you cannot display cname and other columns at the same time.