Only joining rows where the date is less than the max date in another field - sql

Let's say I have two tables. One table containing employee information and the days that employee was given a promotion:
Emp_ID Promo_Date
1 07/01/2012
1 07/01/2013
2 07/19/2012
2 07/19/2013
3 08/21/2012
3 08/21/2013
And another table with every day employees closed a sale:
Emp_ID Sale_Date
1 06/12/2013
1 06/30/2013
1 07/15/2013
2 06/15/2013
2 06/17/2013
2 08/01/2013
3 07/31/2013
3 09/01/2013
I want to join the two tables so that I only include sales dates that are less than the maximum promotion date. So the result would look something like this
Emp_ID Sale_Date Promo_Date
1 06/12/2013 07/01/2012
1 06/30/2013 07/01/2012
1 06/12/2013 07/01/2013
1 06/30/2013 07/01/2013
And so on for the rest of the Emp_IDs. I tried doing this using a left join, something to the effect of
left join SalesTable on PromoTable.EmpID = SalesTable.EmpID and Sale_Date
< max(Promo_Date) over (partition by Emp_ID)
But apparently I can't use aggregates in joins, and I already know that I can't use them in the where statement either. I don't know how else to proceed with this.

The maximum promotion date is:
select emp_id, max(promo_date)
from promotions
group by emp_id;
There are various ways to get the sales before that date, but here is one way:
select s.*
from sales s
where s.sales_date < (select max(promo_date)
from promotions p
where p.emp_id = s.emp_id
);

Gordon's answer is right on! Alternatively, you could also do a inner join to a subquery to achieve your desired output like this:
SELECT s.emp_id
,s.sales_date
,t.promo_date
FROM sales s
INNER JOIN (
SELECT emp_id
,max(promo_date) AS promo_date
FROM promotions
GROUP BY emp_id
) t ON s.emp_id = t.emp_id
AND s.sales_date < t.promo_date;
SQL Fiddle Demo

Related

Access Query: Subtract last 2 values, specific to ID

Help appreciated! My table is setup as follows:
fake data TableName = GAD7
[PatientID Date Value
Sam 10/21/2022 15
George 06/12/2022 7
Luke 09/03/2021 11
Sam 05/15/2020 20
George 12/02/2017 2
George 01/01/1992 6][1]
So I have potentially multiple rows of the same patient, w/different dates.
I need to create a query that subtracts the LAST 2/most recent values for each patient.
So my query would show only those with 2+ records. Negative values are fine/expected.
My successful query would then show:
PatientID (LastScore - 2nd_toLastScore)
Sam -5.0
George 5.0
Luke is not shown because he only has one value
I was able to formulate a query to show only those PatientIDs with >= 2 records and last date and last value. I am not sure how to get the second from last date/value AND THEN subtract those values.
Access query
The SQL view :
SELECT GAD7.PatientID, Count(GAD7.PatientID) AS CountOfPatientID, Last(GAD7.TestDate) AS LastDate, Last(GAD7.Score) AS LastScore
FROM GAD7
GROUP BY GAD7.PatientID
HAVING (((Count(GAD7.PatientID))>=2))
ORDER BY GAD7.PatientID;
Consider:
Query1: Score1
SELECT GAD7.*
FROM GAD7
WHERE 1=(SELECT Count(*)+1 FROM GAD7 AS G7
WHERE G7.PatientID=GAD7.PatientID AND G7.TestDate>GAD7.TestDate);
Query2: Score2
SELECT GAD7.*
FROM GAD7
WHERE 2=(SELECT Count(*)+1 FROM GAD7 AS G7
WHERE G7.PatientID=GAD7.PatientID AND G7.TestDate>GAD7.TestDate);
Query3:
SELECT Score2.PatientID, [Score2].[Score]-[Score1].[Score] AS D
FROM Score1 INNER JOIN Score2 ON Score1.PatientID = Score2.PatientID;
Could nest the SQL statements for an all-in-one query.
Or this all-in-one version using TOP N to pull previous Score:
SELECT GAD7.*, (SELECT TOP 1 Score FROM GAD7 AS Dupe
WHERE Dupe.PatientID = GAD7.PatientID AND Dupe.TestDate<GAD7.TestDate
ORDER BY Dupe.TestDate DESC) AS PrevScore
FROM GAD7 WHERE PatientID IN
(SELECT PatientID FROM GAD7 GROUP BY PatientID HAVING Count(*)>1)
AND 1=(SELECT Count(*)+1 FROM GAD7 AS G7 WHERE G7.PatientID=GAD7.PatientID AND G7.TestDate>GAD7.TestDate);

How to GROUP BY and aggregate fields after JOINS in Query

I have the following data which I got from the following query:
date
quantity
name
season_id
contract_id
signing_date
1
2016-07-01 00:00:00
3
John Doe
4
3000
2016-10-20
2
2021-07-28 00:00:00
14
John Doe
5
3541
2021-01-28
3
2016-08-15 00:00:00
10
John Doe
5
3000
2016-10-20
4
2016-08-02 00:00:00
5
John Doe
5
1528
2016-03-02
WITH ws AS (select date, quantity,
name, season_id, contract_id, contract.signing_date
FROM warehouse_state
JOIN inventory ON inventory.id = warehouse_state.inventory_id
JOIN owner ON owner.inventory_id = warehouse_state.id
JOIN season ON season.id = owner.season_id
JOIN contract ON contract.id = warehouse_contract.contract_id
GROUP BY date, quantity, name, season.id, contract.id, signing_date)
Now, I am having trouble aggregating the ws records based on dates.
Let's say I want a SUM of quantity grouped by date where date is date before contract signing_date. Not sure how to proceed with this, and probably it can be done in a single query without having a WITH x AS query or something actually using it like:
SELECT * FROM ws
LEFT JOIN contract on contract.contract_id = ws.contract_id
-- Here set following condition: for any ws record that has `date` before `signing_date`, SUM quantity and return aggregate
Expected output:
contract_id
signing_date
quantity
name
3000
2016-10-20
18
John Doe
3541
2021-01-28
18
John Doe
1528
2021-01-28
0
John Doe
In the expect output quantity is a SUM, and the record is grouped by contract. In the first record, #1, #3, and #4 were aggregated because their date values are before the contract (3000) signing_date. Even though, the 4th record does not have the same contract_id, it's also aggregated because its date field is before the signing date in contract 3000. Similarly, when grouped by contract 3541, record #2 is excluded from the aggregation because its date value is not before the signing_date of contract 3541.
Any suggestions? Thanks
Does that SQL really compile? Reason is I see you referencing an inventory table that I don't see anywhere.
Also you are grouping on all columns -- essential a "select distinct." Is that what you meant to do?
That aside, assuming your joins are correct and a couple of other assumptions, I'm going to sub them all with "< your tables and joins >." I think all you want is a simple aggregate. No need for a CTE (with clause).
select
date, sum (quantity)
FROM
< your tables and joins >
where
date < signing_date
GROUP BY
date
Alternatively, you can see the total quantity for all dates AND the total quantity before the contract date using a filter:
select
date, sum (quantity) as total_quantity,
sum (quantity) filter (where date < signing_date) as qty_before_contract_sign
FROM
< your tables and joins >
GROUP BY
date
If you wanted to see the other columns as well, then you want a windowing function. Let me know if that's the case and I can demonstrate.
-- EDIT 9/7/22 --
Based on your update, I think this is what you want:
select
contract_id, contract.signing_date, sum (quantity) as quantity,
name
FROM warehouse_state
JOIN inventory ON inventory.id = warehouse_state.inventory_id
JOIN owner ON owner.inventory_id = warehouse_state.id
JOIN season ON season.id = owner.season_id
JOIN contract ON contract.id = warehouse_contract.contract_id
where
date < contact.signing_date
GROUP BY
contract_id, contract.signing_date, name
But the one gotcha is Contract 1528 will not show up in this output since it's filtered out by the where condition.
I'm not fond of this, but you could keep the filter to overcome this... maybe there's a better solution.
select
contract_id, contract.signing_date,
coalesce (sum (quantity) filter (where date < contact.signing_date), 0) as quantity,
name
FROM warehouse_state
JOIN inventory ON inventory.id = warehouse_state.inventory_id
JOIN owner ON owner.inventory_id = warehouse_state.id
JOIN season ON season.id = owner.season_id
JOIN contract ON contract.id = warehouse_contract.contract_id
GROUP BY
contract_id, contract.signing_date, name
Also, my output does not match yours, but I'm hoping that's because of sample data.

Find status from separate fact table

This is in snowflake, but I can figure out any other syntax that is similar.
I have a table of employees and the date they received a paycheck
EMP_ID
Check_dt
1
10-7-2021
2
9-28-2021
1
3-1-2021
And a table that has employee history for full/part time status changes, and the date the change was made effective
EMP_ID
Status
Effective Date
1
F
1/1/20201
2
P
1/1/2021
1
P
6/1/2021
(In this example, employee 1 changed from full to part time halfway through the year)
I want to get the status (F/P) of the employee at the time they received their paycheck.
The output I want is:
EMP_ID
Check_dt
Status
1
10-7-2021
P
2
9-28-2021
P
1
3-1-2021
F
Thanks in advance!
Try this
Select empid, case when t1.chkdate<= (Select
max(effect_date) from
table2 where empid=t1.empid) then t1.status) end as
status
From table1 t1

How to get a correlated subquery as column

I dont know how I can do this sql query, probably its simple but I don't know how i can do it.
I have 2 tables:
Table_Articles:
COD NAME
1 Bottle
2 Car
3 Phone
Table_Articles_Registered
COD_ARTICLE DATE
1 05/11/2014
1 06/11/2014
1 07/11/2014
2 08/11/2014
2 09/11/2014
3 05/11/2014
I want take in the table Table_Articles_Registered the row with the MAX date , finally I want get this result:
COD NAME DATE
1 Bottle 07/11/2014
2 Car 09/11/2014
3 Phone 05/11/2014
I need use the sencente like this. The problem its in the subquery. Later I use other inner join in the sentence, this is only a fragment.
select
_Article.Code,
_Article.Description ,
from Tbl_Articles as _Article left join
(
select top 1 *
from ArticlesRegisterds where DATE_REGISTERED <= '18/11/2014'
order by DATE_REGISTERED
)
as regAux
on regAux.CODE_ARTICLE= _Article.CODE
I dont know how can I connect the field CODE_ARTICLE in the table ArticlesRegisterds with the first query.
I think this is a basic aggregation query with a join:
select a.cod, a.name, max(ar.date) as date
from Artiles a join
ArticlesRegisterds ar
on ar.cod_article = a.cod
group by a.cod, a.name
Try this:-
SELECT TAR.COD_ARTICLE, TA.NAME, MAX(TAR.DATE)
FROM Table_Articles_Registered TAR JOIN
Table_Articles.TA ON TAR.COD_ARTICLE = TA.COD
GROUP BY TAR.COD_ARTICLE, TA.NAME;
Can't you just do this?:
SELECT
Table_Articles.COD,
Table_Articles.NAME,
(
SELECT MAX(Table_Articles_Registered.DATE)
FROM Table_Articles_Registered
WHERE Table_Articles.COD_ARTICLE=Table_Articles.COD
) AS DATE
FROM
Table_Articles

SQL Inner Join query

I have following table structures,
cust_info
cust_id
cust_name
bill_info
bill_id
cust_id
bill_amount
bill_date
paid_info
paid_id
bill_id
paid_amount
paid_date
Now my output should display records (1 jan 2013 to 1 feb 2013) between two bill_dates dates as single row as follows,
cust_name | bill_id | bill_amount | tpaid_amount | bill_date | balance
where tpaid_amount is total paid for particular bill_id
For example,
for bill id abcd, bill_amount is 10000 and user pays 2000 one time and 3000 second time
means, paid_info table contains two entries for same bill_id
bill_id | paid_amount
abcd 2000
abcd 3000
so, tpaid_amount = 2000 + 3000 = 5000 and balance = 10000 - tpaid_amount = 10000 - 5000 = 5000
Is there any way to do this with single query (inner joins)?
You'd want to join the 3 tables, then group them by bill ids and other relevant data, like so.
-- the select line, as well as getting your columns to display, is where you'll work
-- out your computed columns, or what are called aggregate functions, such as tpaid and balance
SELECT c.cust_name, p.bill_id, b.bill_amount, SUM(p.paid_amount) AS tpaid, b.bill_date, b.bill_amount - SUM(p.paid_amount) AS balance
-- joining up the 3 tables here on the id columns that point to the other tables
FROM cust_info c INNER JOIN bill_info b ON c.cust_id = b.cust_id
INNER JOIN paid_info p ON p.bill_id = b.bill_id
-- between pretty much does what it says
WHERE b.bill_date BETWEEN '2013-01-01' AND '2013-02-01'
-- in group by, we not only need to join rows together based on which bill they're for
-- (bill_id), but also any column we want to select in SELECT.
GROUP BY c.cust_name, p.bill_id, b.bill_amount, b.bill_date
A quick overview of group by: It will take your result set and smoosh rows together, based on where they have the same data in the columns you give it. Since each bill will have the same customer name, amount, date, etc, we are fine to group by those as well as the bill id, and we'll get a record for each bill. If we wanted to group it by p.paid_amount, though, since each payment would have a different one of those (possibly), you'd get a record for each payment as opposed to for each bill, which isn't what you'd want. Once group by has smooshed these rows together, you can run aggregate functions such as SUM(column). In this example, SUM(p.paid_amount) totals up all the payments that have that bill_id to work out how much has been paid. For more information, please look at W3Schools chapter on group by in their SQL tutorials.
Hope I've understood this correctly and that this helps you.
This will do the trick;
select
cust_name,
bill_id,
bill_amount,
sum(paid_amount),
bill_date,
bill_amount - sum(paid_amount)
from
cust_info
left outer join bill_info
left outer join paid_info
on bill_info.bill_id=paid_info.bill_id
on cust_info.cust_id=bill_info.cust_id
where
bill_info.bill_date between X and Y
group by
cust_name,
bill_id,
bill_amount,
bill_date