SQL Combining two queries - sql

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

Related

Get max date for user from another table

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.

PostgreSQL: how to get max() of GROUP BY?

I have a table called history with the fields id, lastname, event and date.
It saves events of persons that works in my office like "Entering office", "Exiting office", "Illness", "Holidays", etc.
I need to get the last event of every person.
This is what I tried but it doesn't work:
SELECT lastname, event, max(date)
FROM personshistory
GROUP BY lastname, event;
You can use distinct on:
select distinct on (lastname) ph.*
from personshistory
order by lastname, date desc;
distinct on is a very convenient Postgres extension. It keeps one row for each value of the expressions in parentheses. The specific row is determined by the order by clause -- based on the keys that follow the distinct on keys.
With NOT EXISTS:
SELECT p.lastname, p.event, p.date
FROM personshistory p
WHERE NOT EXISTS (
SELECT 1 FROM personshistory
WHERE lastname = p.lastname and date > p.date
)
or join your query (without the event column) to the table:
SELECT p.lastname, p.event, p.date
FROM personshistory p INNER JOIN (
SELECT lastname, MAX(date) maxdate
FROM personshistory
GROUP BY lastname
) g on g.lastname = p.lastname and g.maxdate = p.date

Join table on null where condition

I have tables Member and Transaction. Table Member has 2 columns MemberID and MemberName. Table Transaction has 3 columns, MemberID, TransactionDate, and MemberBalance.
The rows in the tables are as shown below:
Table Member:
MemberID MemberName
=============================
1 John
2 Betty
3 Lisa
Table Transaction:
MemberID TransactionDate MemberBalance
=====================================================
1 13-12-2012 200
2 12-12-2012 90
1 10-09-2012 300
I would like to query for MemberID, MemberName and MemberBalance where the TransactionDate is the latest (max) for each MemberID.
My query is like this:
SELECT
t.MemberID, m.MemberName , t.MemberBalance
FROM
Member AS m
INNER JOIN
Transaction AS t ON m.MemberID = t.MemberID
WHERE
t.TransactionDate IN (SELECT MAX(TransactionDate)
FROM Transaction
GROUP BY MemberID)
This query returns:
MemberID MemberName MemberBalance
===================================================
1 John 200
2 Betty 90
My problem is, I want the query to return:
MemberID MemberName MemberBalance
===================================================
1 John 200
2 Betty 90
3 Lisa NULL
I want the member to be displayed even if its MemberID does not exist in the Transaction table.
How do I do this?
Thank you.
You can also use something like this:
SELECT m.MemberID, m.MemberName, t1.MemberBalance
FROM Member AS m
LEFT JOIN
(
select max(transactionDate) transactionDate,
MemberID
from Transactions
group by MemberID
) AS t
ON m.MemberID = t.MemberID
left join transactions t1
on t.transactionDate = t1.transactionDate
and t.memberid = t1.memberid
See SQL Fiddle with Demo
member to be displayed even if its MemberID does not exist in Transaction table
You can preserve rows using LEFT JOIN on the Member table to Transaction table.
where the TransactionDate is the latest (max) for each MemberID.
From SQL Server 2005 onwards, the preferred and better performing method is to use ROW_NUMBER()
SELECT MemberID, MemberName, MemberBalance
FROM (
SELECT m.MemberID, m.MemberName , t.MemberBalance,
row_number() over (partition by m.MemberID order by t.TransactionDate desc) rn
FROM Member AS m
LEFT JOIN [Transaction] AS t ON m.MemberID = t.MemberID
) X
WHERE rn=1;
To keep member in the result set, you need an outer join.
Also, please don't forget to add a condition on memberid for inner select query, as you might get issues when a maximum date for one user would match a non-maximum date of another (your where condition would pass twice for the second user as his transaction dates would appear on the select's results twice, one would be his actual maximum date and another - the max date of some user matching a non-max date)
You need to use LEFT JOIN. Also you had an error in your query because if two members had transactions at the same time you can get two rows for both the users.
Try this
SELECT t.MemberID, m.MemberName , t.MemberBalance
FROM Member AS m
LEFT JOIN Transaction AS t ON m.MemberID = t.MemberID AND t.TransactionDate=
(
SELECT MAX(TransactionDate)
FROM Transaction T2
WHERE T2.MemberID=t.MemberID
)
SELECT a.MemberId,a.MemberName,a.MemberBalance
FROM
(
SELECT m.MemberId,m.MemberName,t1.MemberBalance
,ROW_NUMBER() OVER(PARTITION BY m.MemberId ORDER BY t1.TransactionDate DESC) AS RN
FROM
#Member m OUTER APPLY (SELECT t.MemberId,t.MemberBalance,t.TransactionDate
FROM #Transaction t WHERE m.MemberId=t.MemberId) t1
)a
WHERE a.RN=1

SQL to find the most recent account transaction for each customer

EDIT: I'm using SQL Server
I looked around for an example of this but couldn't find anything so I'll start a new thread...
I have 3 tables.
Account
AccountID
FirstName
LastName
AccountEnroll
AccountEnrollID
AccountID
AccountTypeID
EnrollDate
AccountType
AccountTypeID
AccountType
The AccountEnroll table is a bridge table to track each customer's enrollment history. I want to use the "EnrollDate" column to determine the current account type for each customer. I need to write a SELECT statement that can display AccountID, FirstName, LastName, (current)AccountType.
I am having trouble getting my resultset to display only the MAX(EnrollDate) record for each customer.
You can use common table expressions to do this pretty simply.
with cte as (
select A.FirstName, A.LastName, AT.AccountType, AE.EnrollDate, row_number() over (partition by AE.AccountID order by EnrollDate desc) as [rn]
from Account as A
inner join AccountEnrolled as AE
on A.AccountId = AE.AccountId
inner join AccountType as AT
on AE.AccountTypeId = AT.AccountTypeId
)
select FirstName, LastName, AccountType, EnrollDate
from cte
where rn = 1
Try this:
SELECT
a.AccountID, a.FirstName, a.LastName,
at.AccountType AS 'Current AccountType'
FROM Account a
INNER JOIN
(
SELECT AccountID, MAX(EnrollDate) MaxDate
FROM AccountEnroll
GROUP BY AccountID
) t
INNER JOIN AccountEnroll ae ON ae.AccountID = t.AccountID
AND ae.EnrollDate = t.MaxDate
INNER JOIN AccountType at ON ae.AccountTypeID = at.AccountTypeID
You could use a correlated sub-query:
SELECT A.AccountId, A.FirstName, A.LastName, AT.AccountType
FROM Account A
JOIN AccountEnroll AE
ON A.AccountId = AE.AccountId
JOIN AccountType AT
ON AE.AccountTypeId = AT.AccountTypeId
WHERE NOT EXISTS (
SELECT 1
FROM AccountEnroll
WHERE AccountId = AE.AccountId
AND EnrollDate > AE.EnrollDate
)

SQL Server 2005 Query remove duplicates via date

I searched and searched and can't seem to figure out this issue:
We have three tables which have data I need to collect and show in a view.
SELECT
C.FirstName, C.LastName,
aspnet_Membership.LoweredEmail,
MAX(Bill.Code) AS BCodes,
MAX(Bill.BillDate)
FROM
dbo.Client C
INNER JOIN
dbo.Bill ON C.Id = Bill.BId
INNER JOIN
dbo.aspnet_Membership ON aspnet_Membership.UserId = C.UserGUID
WHERE
((Bill.Code='ASDF'
OR Bill.Code='XYZ'
OR Bill.Code='QWE'
OR Bill.Code='JKL')
AND C.LastName!='Unassigned')
GROUP BY
LastName, FirstName, LoweredEmail, Code, BDate
Client table has: FirstName LastName and UserGuid
Bill table has: BCode, BillDate
aspnet_Membership table has: E-mail, UserId
RESULTS:
FirstName LastName E-mail BCode BillDate
FName1 Lname1 fname#isp.com XYZ 2010-05-13 00:00:00.000
Fname2 Lname2 fname2#isp2.com XYZ 2010-06-05 00:00:00.000
Fname2 Lname2 fname2#isp2.com ASD 2008-09-17 12:01:45.407
As you can see Fname2 shows up twice, only difference is in the BCode and BillDate.
How can I make this go with the latest date so I get Fname2 record with Bcode of XYZ with date of 2010-06-05.
Any help would be appreciated, thank you in advance.
Seeing that you're using SQL Server 2005, I would probably use a CTE (Common Table Expression) to do this - something like:
;WITH MyData AS
(
SELECT
c.FirstName, c.LastName,
asp.LoweredEmail,
b.Code AS BCodes, b.BillDate,
ROW_NUMBER() OVER (PARTITION BY c.LastName,c.FirstName
ORDER BY BillDate DESC) AS 'RowNum'
FROM
dbo.Client c
INNER JOIN
dbo.Bill b ON C.Id = b.BId
INNER JOIN
dbo.aspnet_Membership asp ON asp.UserId = c.UserGUID
WHERE
b.Code IN ('ASDF', 'JKL', 'QWE', 'XYZ')
AND c.LastName != 'Unassigned'
)
SELECT
FirstName, LastName, LoweredEmail, BCodes, BillDate
FROM
MyData
WHERE
RowNum = 1
This CTE with the ROW_NUMBER() clause will:
"partition" your data by (FirstName,LastName) - each pair of those values gets a new sequential "row number"
order those values within each partition by descending BillDate
So the resulting set of data has each newest entry for any (FirstName,LastName) group with RowNum = 1 - and that's the data I'm selecting from that CTE.
Does that work for you??
Perform a second join (using a LEFT JOIN) to find a later row in Bill table, and then filter any results where that join succeeds:
SELECT
C.FirstName, C.LastName,
aspnet_Membership.LoweredEmail,
MAX(Bill.Code) AS BCodes,
MAX(Bill.BillDate)
FROM dbo.Client C
INNER JOIN dbo.Bill
ON C.Id=Bill.BId
INNER JOIN dbo.aspnet_Membership
ON aspnet_Membership.UserId=C.UserGUID
LEFT JOIN dbo.Bill b2
ON Bill.BId = b2.BId and
b2.Code in ('ASDF','XYZ','QWE','JKL') and
b2.BDate > Bill.BDate
WHERE
b2.BId is null and
((Bill.Code='ASDF'
OR Bill.Code='XYZ'
OR Bill.Code='QWE'
OR Bill.Code='JKL')
AND C.LastName!='Unassigned')
GROUP BY LastName, FirstName, LoweredEmail, Code, BDate