I have to tables called USERS and COMM_HISTORY
USERS(user_id, name)
COMM_HISTORY(comm_history_id, comm_date, comm_by, user_id)
USERS table will have a unique list of users and COMM_HISTORY can have duplicates of user_id. When I join them I would like to grab the name of user and the most recent comm_date from COMM_HISTORY.
Something similar with this post sql join - only select top row from 2nd table but it seems there's no correct answers in that post.
I would like to have the ff result when I join them
1 John Doe 2012-01-29 Jane Doe
Instead of
1 John Doe 2011-10-20 Jane Lee
2 John Doe 2012-01-29 Jane Doe
3 John Doe 2011-09-08 Jane Doe
Anyone has a solution for this?
#Andomar's solution in the other question should work. Adapted to your use case, it would be:
SELECT USERS.*, COMM_HISTORY.*
FROM USERS
INNER JOIN COMM_HISTORY
ON USERS.user_id = COMM_HISTORY.user_id
LEFT JOIN COMM_HISTORY LATER_HISTORY
ON COMM_HISTORY.user_id = LATER_HISTORY.user_id
AND LATER_HISTORY.comm_date > COMM_HISTORY.comm_date
WHERE LATER_HISTORY.user_id IS NULL
Join the history table back onto itself, and set a condition saying no later history rows should be selected.
an approach using cte
with recent_comms as (
select
row_number() over (partition by user_id, order by comm_date decs) as rn,
comm_date,
comm_by,
user_id
FROM comm_history)
select
users.name,
recent_comms.comm_date,
recent_comms.comm_by
from users
join recent_comms on users.user_id = recent_comms.user_id
and recent_comms.rn = 1
changing the rn = 1 to rn <= 5 would get the last 5 per user, for example.
Related
I'm a novice when it comes to SQL, so forgive me if this is a dumb question.
I have 2 tables, one with a list of users, and one that holds email history data.
Users Table:
userID fName lName ...
1 John Smith
2 Jane Doe
3 Kevin Cooper
Email History Table:
emailID userID subject sendDate ...
1 6 welcome 2020-10-17
2 3 hello 2020-10-20
3 7 welcome 2020-10-23
I am wanting to do some sort of select statement that would compare every customer in table 1, to every email in table 2 based on some sort of search query (in this case where subject = "hello" and sendDate = "2020-10-20" and would return something like this:
Returned Query:
userID fName lName ... emailSent?
1 John Smith ... No
2 Jane Doe ... No
3 Kevin Cooper ... Yes
One option uses exists and a correlated subquery:
select u.*,
exists (
select 1
from emailhistory eh
where eh.userid = u.userid and eh.subject = 'hello' and eh.senddate = '2020-20-20'
) emailSent
from users u
This gives you 0/1 values in column emailSent, where 1 indicates that a match exists. As compared to the left join approach, the upside is that it does not "multiplies" the user rows if more than one match is found in the history table.
For performance, consider an index on emailhistory(userid, subject, senddate).
You can left join the email table on, putting your date and subject criteria in the where clause:
SELECT
u.userid,
u.fname,
u.lname,
case when eh.emailid is null then 'No' else 'Yes' end as emailsent
FROM
users u
LEFT JOIN
emailhistory eh
ON
u.userid = eh.emailid AND
eh.subject = 'hello' AND
eh.senddate = '2020-10-20'
This conceptually filters the email table down to just that subject and day, then joins those records onto the users table. You get every row from users and only rows from emailhistory that match on userid, and also had that subject/date. You can then examine whether the emailid (a key of the join) is null or not; the only way it can be null is if no email was sent to that user on that date with that subject
I have created a simple Access database an example of table structure and a query. Two tables, person(contains 3 records) & address(contains 5 records), provide the ability to capture multiple addresses for each person. I am good with normal conditional statements, but this one is throwing me for a loop...
I am looking for a query that will return only the newest address for a given person.
Table Relationship
Current sql for the query:
SELECT Person.PersonID_PK, Address.Address, Address.StatusDate
FROM Person INNER JOIN Address ON Person.[PersonID_PK] = Address.PersonID_FK;
My current returns:
EmployeeID_PK Address StatusDate
1 12 Elm St, MN 23569 11/13/2017
1 15 Apple Ln, NY 12345 7/15/2018
2 30 Mulberry, TN 38456 6/11/2018
2 10 Lonesome Pine, KY 15487 12/4/2018
3 100 Plaze Place, LA 14563 6/17/2018
I need to return each person along with the greatest(newest) StatusDate
My expected return should be:
EmployeeID_PK Address StatusDate
1 15 Apple Ln, NY 12345 7/15/2018
2 10 Lonesome Pine, KY 15487 12/4/2018
3 100 Plaze Place, LA 14563 6/17/2018
You can use a correlated subquery:
select a.*
from address as a
where a.statusdate = (select max(a2.statusdate)
from address as a2
where a2.EmployeeID_PK = a.EmployeeID_PK
);
Using a CTE with a ranking function works neatly.
;WITH empaddress AS
(
SELECT person.personid_pk,
address.address,
address.statusdate,
Dense_rank() OVER (partition BY id ORDER BY statusdate DESC) AS d_rank
FROM person
INNER JOIN address
ON person.[PersonID_PK] = address.personid_fk; )
SELECT person.personid_pk,
address.address,
address.statusdate
FROM empaddress
WHERE d_rank = 1;
Thanks for the help. I modified Gordon's code to support my query, the following provided the answer.
SELECT Person.PersonID_PK, Address.Address, Address.StatusDate
FROM Person
INNER JOIN Address
ON Person.[PersonID_PK] = Address.PersonID_FK
WHERE (((Address.StatusDate)=(SELECT MAX(Address.StatusDate)
FROM Address
WHERE Person.PersonID_PK = Address.PersonID_FK)));
I've tried some of the other solutions I've found on SO, but they don't seem to work with my schema, or I just suck at SQL. So let's say I have two tables, table 1 is something like this:
LastName | FirstName | Date
Doe John 7/07/14
Doe John 7/07/14
Doe John 7/08/14
Bond James 7/07/14
Bond James 7/09/14
Jane Mary 7/08/14
Essentially, a person will have an entry for a certain date. they can also have multiple entries for one date. table 2 is a range of dates, such as:
Date
7/06/14
7/07/14
7/08/14
7/09/14
What I want to do is get a result set that shows for what days in table 2 is a person missing an entry, ideally with the person's name as well. Any leads? Thanks!
Try this,
;WITH CTE AS
(
--create list of all names and date combinations from both table
SELECT DISTINCT A.LastName, A.FirstName, B.Date_col
FROM Table1 A, Table2 B
)
--select rows that are missing dates in your first table
SELECT X.* FROM CTE X
LEFT OUTER JOIN Table1 Y
ON X.LastName = Y.LastName
AND X.FirstName = Y.FirstName
AND X.Date_col = Y.Date_Col
WHERE Y.LastName IS NULL
I'm trying to get all phones, emails, and organizations for a person and show it in a flat file format. There should be n number of rows, where n is the max count of organizations, emails, or phones. NULL values will be shown once all values have been shown in the rows, with NULL being the last values. The emails and phones can only have 1 PreferredInd per person. I want these to be on the same row (1 of them can be NULL). I've tried to do this on a more complex query, but couldn't get it to work, so I've started over using this simpler example.
Example tables and values:
#ContactPerson
Id Name
1 John Doe
#ContactEmail
Id PersonId Email PreferredInd
1 1 johndoe#us.gov 0
2 1 jdoe#us.gov 1
3 1 johndoe#gmail.com 0
#ContactPhone
Id PersonId Phone PreferredInd
1 1 888-867-5309 0
2 1 305-476-5234 1
#ContactOrganization
Id PersonId Organization
1 1 US Government
2 1 US Army
I want a resulting set to look like:
Name Organization PreferredInd Email Phone
John Doe US Government 1 jdoe#us.gov 888-867-5309
John Doe US Army 0 johndoe#us.gov 305-467-5234
John Doe NULL 0 johndoe#gmail.com NULL
The complete sql code that I have for this example is here on pastebin. It also includes code to create the sample tables. It works when the count of emails exceeds the count of organizations or phones, but that won't always be true. I can't seem to figure out how to get the result that I'm looking for. The actual tables I'm working with can have 0 or infinity emails, phones, or organizations per person. There will also be many more values, but I can fix that myself.
Can you help me fix my query or show me a simpler way to do it? If you have any questions, just let me know and I can try to answer them.
something like this?
with cte_e as (
select
*,
row_number() over(order by PreferredInd desc, Id) as rn
from ContactEmail
), cte_p as (
select
*,
row_number() over(order by PreferredInd desc, Id) as rn
from ContactPhone
), cte_o as (
select
*,
row_number() over(order by Organization) as rn
from ContactOrganization
), cte_d as (
select distinct rn, PersonId from cte_e union
select distinct rn, PersonId from cte_p union
select distinct rn, PersonId from cte_o
)
select
pr.Name, o.Organization, e.Email, p.Phone
from cte_d as d
left outer join ContactPerson as pr on pr.Id = d.PersonId
left outer join cte_e as e on e.PersonId = d.PersonId and e.rn = d.rn
left outer join cte_p as p on p.PersonId = d.PersonId and p.rn = d.rn
left outer join cte_o as o on o.PersonId = d.PersonId and o.rn = d.rn
sql fiddle demo
it's a bit clumsy, I can think of couple of other possible ways to do this, but I think this one is most readable one
Step 1
Write a query that does the full join of all the tables, which will end up with lots of duplicate rows for each person (for each email or phone number)
Step 2
Write a second query that uses GroupBy to group the rows, and that uses the Case or Decode keywords (like a c# switch statement) to find the preferred row value and select it as the value to display
I have two tables and one reference table for the query. Any suggestion or help would greatly appreciated.
table1
user_id username firstname lastname address
1 john867 John Smith caloocan
2 bill96 Bill Jones manila
table2
user_name_id username firstname lastname address designation
1 jakelucas Jake Lucas caloocan employee
2 jadejones Jade Jones Quezon student
3 bong098 Bong Johnson pasig employee
reference table
ref_id username friend_username
1 tirso bill96
2 tirso jadejones
2 tirso bong098
the output should like this
user_id user_name_id username firstname lastname address designation
2 bill96 Bill Jones manila
2 jadejones Jade Jones Quezon student
3 bong098 Bong Johnson pasig employee
Since some decent union queries have already been posted, I'll talk about your db design a little bit.
I would definitely take what IronGoofy said into serious consideration before you take too much time looking into joining these tables together. It seems that you have a lot of duplicate data to manage with your tables, and that could get out of hand rather quickly should this scale up.
I think you should probably try and separate your data out so that the important information can be linked on the user_id.
So, for instance, you could have a few tables here...
User Information Table:
---------
User_id
Username
First Name
Last Name
Address
Designation_id
Friend Link Table:
---------
Friend_link_id
User_id
Friend_user_id
Designation Table:
---------
Designation_id
Designation_name
So, rather than link on your user names all over the place, you would simply join on the various ID's. A bit cleaner and missing the duplicate data issue that you had before IMO. Hope this helps...
Can you try something like this
SELECT [table1].[USER_ID],
NULL user_name_id,
[table1].username,
[table1].firstname,
[table1].lastname,
[table1].address,
NULL designation
FROM reference_table INNER JOIN
table1 ON [reference_table].friend_username = [table1].username
UNION
SELECT NULL USER_ID,
[table2].user_name_id,
[table2].username,
[table2].firstname,
[table2].lastname,
[table2].address,
[table2].designation
FROM reference_table INNER JOIN
table2 ON [reference_table].friend_username = [table2].username
It's not quite clear what you are trying to achieve, but here's a guess:
SELECT user_id, NULL as user_name_id, username, ...
FROM ref_tab r join table1 t1 on r.friend_username = t1.username
WHERE r.ref_id = 1
UNION
SELECT NULL as user_id, user_name_id, username, ...
FROM ref_tab r join table1 t2 on r.friend_username = t2.username
WHERE r.ref_id = 2
But I'd have a hard look at the DB design and think about some improvements ...