Get max date for user from another table - sql

I have two tables, in one table I am storing user statuses and in the second logs.
"status" table
id , customerId, userName, serviceId, status
"logs" table
id, customerId, logDate, status
I need to get the latest log for each customer for specific date intervals (from 2020-10-01 to 2020-11-31) and specific status (status = 6). All customer logs are stored in the "logs" table.
This is what I tried but no luck:
Select distinct (a.customerId), a.userName, a.serviceId, a.status, max(logDate)
FROM status a
JOIN logs b
WHERE logDate BETWEEN '2020-10-01' AND '2020-11-31' and a.customerId = b.customerId and a.status = 6 group by b.logDate
Any help would be appreciated.

Your group by clause is off: you would need to group by all non-aggregated columns.
select s.customerid, s.username, s.serviceid, s.status, max(l.logdate) as maxlogdate
from status s
inner join logs l
where
l.logdate >= '2020-10-01'
and l.logdate < '2020-12-01'
and l.customerid = s.customerid
and s.status = 6
group by s.customerid, s.username, s.serviceid, s.status
Some databases support just putting the primary key of the status table in the group by clause. Assuming that it is customerid:
group by s.customerid
Note that I changed the query to use meaningful table aliases.

Related

How can I unite these two related queries?

I have this query to check if a person is a customer or have been:
SELECT DISTINCT ON (person_id) person_id, person.name,
(CASE WHEN status = 'inactive' then 'Ex-Customer'
WHEN status = 'active' then 'Customer'
END) AS account_status
FROM person_subscription
INNER JOIN person ON person_subscription.person_id = person.id
ORDER BY person_id, status ASC
And I have this other query to get the locations:
SELECT person_id, string_agg(name, ';' ORDER BY person_id)
FROM person_location WHERE person_id IN
(SELECT person_id FROM person_subscription WHERE status IS NOT NULL)
GROUP BY person_id;
How can I unite them and show person location as a single row on the first query?
If I follow this correctly, you can use lateral joins:
select p.id as person_id, p.name, pl.*, ps.*
from person p
cross join lateral (
select string_agg(pl.name, ';' order by pl.name) as as person_locations
from person_location pl
where pl.person_id = p.id
) pl
cross join lateral (
select
case status
when 'inactive' then 'ex-customer'
when 'active' then 'customer'
end as account_status
from person_subscription ps
where ps.person_id = p.id
order by ps.??
limit 1
) ps
As commented already, your original first query is missing an order by clause, which makes it undefined which subscription status will be chosen where there are several matches. This translates as order by ps.?? in the second subquery, which you would need to replace with the relevant column name.
Another flaw, that time in the second query in your question, is that the order by clause of string_agg() is not deterministic (all rows in the group have the same person_id). I ordered by location name instead, you can change that to some other column if you like.
You would join it in:
SELECT DISTINCT ON (ps.person_id) ps.person_id, ps.person.name,
(CASE WHEN ps.status = 'inactive' then 'Ex-Customer'
WHEN ps.status = 'active' then 'Customer'
END) AS account_status
FROM person_subscription ps INNER JOIN
person p
ON ps.person_id = p.id LEFT JOIN
(SELECT pl.person_id, STRING_AGG(pl.name, ';') as names
FROM person_location pl
GROUP BY pl.person_id
) pl
ON pl.person_id = p.id
ORDER BY ps.person_id, p.status ASC;
I'm not sure what the significance of the WHERE clause is for getting locations, but you can include that in the subquery as well.

SQL select all by date

Im new with SQL.
I have 3 tables like below:
Table client: a list of clients with their first and last names, full address
Table Produit: a list of articles sent on the web site containing the name of the article, the country where it was manufactured, and its price
Table Actions: a list of all transactions done by clients with the time of the transactions.
Output desired
get the revenue generated by all clients where date is 2018 in each country, and ordered by the highest revenue first.
This should work if you want to include the country, see highest revenue first, & use ANSI function for year.
SELECT c.client_id,
c.first_name,
c.last_name,
p.country,
SUM(a.amount) amount
FROM client c
JOIN actions a ON c.client_id = a.client_id
JOIN produit p ON a.article_id = p.article_id
WHERE EXTRACT(YEAR FROM a.transaction_date) = '2018'
GROUP BY c.client_id,
c.first_name,
c.last_name,
p.country
ORDER BY SUM(a.amount) DESC;
Could do something like this (which assumes that the transactions table has the name of the article, and the produit table has a date field with a date data type):
select
a.client,
sum(c.price) as revenue
from client as a
inner join actions as b
on a.client = b.client
inner join produit c
on c.article = b.article
where year(c.date) = '2018'
group by
a.client
order by
sum(c.price);

How can I select lastest purchase price before a selling date?

I have 5 tables in my database, products, purchase_orders, invoice, invoice_details, and product_prices and their schema are like below.
Table: products
id
trade_name
Table: purchase_orders
id
product_id
created
Table: invoices
id
created
Table invoice_details
id
invoice_id
product_id
price_id
Table product_prices
id
retail_price
effective_from
effective_to
I think that I need to somehow join or check created on purchase_orders to created on invoices. So, I started with getting drug id, invoice date.
select d.id as drug_id
, i.created as invoice_created
, dp.retail_price
from drugs d
inner join invoice_details id
on d.id = id.drug_id
inner join invoices i
on i.id = id.invoice_id
inner join drug_prices dp
on dp.id = id.price_id
The next step is to match created on invoice that I have to created on purchase_orders which I haven't figured it out.
inner join (
select drug_id
, purchase_price
, ISNULL(created, CONVERT(DATETIME, '2015-10-07 01:37:12.370')) as created
from purchase_orders po
) as prepared_po
on prepared_po.created <= i.created
How can I get the lasted purchase price for each item that I sold?
Here's a simplified version of the logic you need (I renamed your columns so that it's easier to see which are which without doing all the intermediary joins you've already written yourself):
;With CTE as (select a.productID, a.saledate, b.price, b.purchasedate
, row_number() over (partition by a.productID, a.saledate
order by b.purchasedate desc) RN
from sales a
left join purchases b
on a.productID = b.productID and a.saledate >= b.purchasedate
)
Select productID, saledate, price, purchasedate
from CTE where RN = 1
Basically, you join to get all purchase records up to the sale date, then use row_number to find the latest one.
http://sqlfiddle.com/#!6/a0f68/2/0

Using "Group By" in a Select Sub-Query

Below is the query that I created, but upon validation of the results produced, the query is producing inaccurate results.
select a.acct_id, c.bill_dt
from account a inner join invoice_detail b on a.acct_id = b.acct_id
inner join
(select acct_id, max(bill_dt) as bill_dt from invoice_detail
where bill_dt < '1/1/2014'
group by acct_id)c on b.acct_id = c.acct_id
and a.acct_stat_id = '275'
and not a.acct_id in (select acct_id from contract where cntrct_stat_id in ('394','554','555','556'))
and not a.acct_id in (select acct_id from billing_adj where bill_adj_stat_id in ('4','394','553','554','555'))
group by a.acct_id, c.bill_dt
order by a.acct_id ASC
I would like my results to only show acct_ids and the max(bill_dt) after meeting all query criteria. The invoice_detail table contains multiple records for an acct_id. However, when I executed the query, I randomly selected an acct_id that had a max(bill_dt) of 12/31/2013 for validation. I looked in the invoice_detail table by acct_id and the results came back with additional records with a bill_dt greater than 1/1/2014. I want to identify acct_ids that do not have any invoices after 1/1/2014.
I want to identify acct_ids that do not have any invoices after 1/1/2014
Then your condition in your subquery needs to be:
HAVING max(bill_dt) < '1/1/2014'
You're also not using the invoice_detail table except in the subquery, so you can take it out of the main query:
select a.acct_id, c.bill_dt
from account a
inner join
( select acct_id, max(bill_dt) as bill_dt from invoice_detail
group by acct_id
HAVING max(bill_dt) < '1/1/2014'
) c on a.acct_id = c.acct_id
and a.acct_stat_id = '275'
and not a.acct_id in (select acct_id from contract where cntrct_stat_id in ('394','554','555','556'))
and not a.acct_id in (select acct_id from billing_adj where bill_adj_stat_id in ('4','394','553','554','555'))
group by a.acct_id, c.bill_dt
order by a.acct_id ASC

SQL Combining two queries

The below code selects records from the two tables where both the email and dob match another record (all duplicates..)
SELECT
AccountName,
EmailAddress,
DateOfBirth
FROM
(
SELECT
a.AccountName,
a.EmailAddress,
u.DateOfBirth,
COUNT(*) over (partition by a.EmailAddress, u.DateOfBirth) AS cnt
FROM Account AS a
JOIN [User] AS u ON a.AccountID = u.AccountID
) ua
WHERE cnt > 1
AND EmailAddress IS NOT null
AND DateOfBirth IS NOT null
ORDER BY EmailAddress, DateOfBirth
I also want to add to this table, a field within another table called 'Audit'. We can join them using the LoginID, however the LoginID has a one to many relationship in the Audit table. i.e. a LoginID can have many Audits.
I want to add the Audit StartDate column. The following query allows me to identify the latest Audit by date.
SELECT a.LoginID as AuditLoginID,
MAX(StartDate) as StartDate
FROM Audit as a
GROUP BY a.LoginID
ORDER BY a.StartDate
Would anyone be able to suggest how I can combine these two queries, so that my original query has a join to the Audit table, displaying a 'StartDate' column of the latest audit start date?
You should consider using a correlated subquery. That will avoid building another database object to support this query, and it's a relatively standard SQL construct.
Example:
SELECT
AccountName,
EmailAddress,
DateOfBirth
FROM
(
SELECT
a.AccountName,
a.EmailAddress,
u.DateOfBirth,
a.LoginID,
COUNT(*) over (partition by a.EmailAddress, u.DateOfBirth) AS cnt
FROM Account AS a
JOIN [User] AS u ON a.AccountID = u.AccountID
) ua
join Audit as a
on a.LoginID = au.LoginID
WHERE cnt > 1
AND EmailAddress IS NOT null
AND DateOfBirth IS NOT null
AND a.startdate = (SELECT MAX(StartDate) as StartDate
FROM Audit as b
WHERE b.LoginID = a.LoginID)
ORDER BY EmailAddress, DateOfBirth
Here's an expansion on my comment:
CREATE VIEW MostRecentLogins AS
(
SELECT a.LoginID as AuditLoginID,
MAX(StartDate) as StartDate
FROM Audit as a
GROUP BY a.LoginID
)
Then, you can join the MostRecentLogins view into your other query. It's not clear from your post which column would be the counterpart to LoginId (from the Audit table) but the query would then look something like this:
SELECT a.AccountName,
a.EmailAddress,
u.DateOfBirth,
MRL.StartDate
FROM
(
SELECT a.AccountName,
a.EmailAddress,
u.DateOfBirth,
COUNT(*) over (partition by a.EmailAddress, u.DateOfBirth) AS cnt
FROM Account AS a
JOIN [User] AS u
ON a.AccountID = u.AccountID
) ua
INNER JOIN MostRecentLogins MRL
ON MRL.LoginID = a.LoginID -- not sure what column should be on the RHS of this..
WHERE cnt > 1
AND EmailAddress IS NOT null
AND DateOfBirth IS NOT null
ORDER BY EmailAddress, DateOfBirth