Sql query to solve the below scenario - sql

I have a table like :
Employeeid personid reltnship status
111 125 owner active
111 252 prevown active
112 3 descsd active
I want to write a query to get all the details for the employees who have reltnship as combination of 'owner' and 'prevown'

Use Self JOIN
SELECT DISTINCT S.*
FROM Tbl S JOIN tbl T ON S.Employeeid = T.Employeeid
where S.reltnship = 'owner' AND T.reltnship = 'prevown'

Use a subquery that returns all the employeeids that satisfy the condition:
select * from tablename
where employeeid in (
select employeeid
from tablename
where reltnship in ('owner', 'prevown')
group by employeeid
having count(distinct reltnship) = 2
)
See the demo.
Results:
| Employeeid | personid | reltnship | status |
| ---------- | -------- | --------- | ------ |
| 111 | 125 | owner | active |
| 111 | 252 | prevown | active |

If you want all details, then one method uses exists:
select t.*
from t
where exists (select 1
from t t2
where t2.employeeid = t.employeeid and
t2.reltnship = 'owner'
) and
exists (select 1
from t t2
where t2.employeeid = t.employeeid and
t2.reltnship = 'prevown'
) ;
This is phrased to be index friendly. The index that you want is on (employeeid, reltnship).
If you only want the employees that meet this criteria, I would recommend aggregation:
select t.employeeid
from t
where reltnship in ('owner', 'prevown')
group by employeeid
having count(distinct reltnship) = 2;

You should study mysql query structure and what are clauses of queries.
Here is query:
select *
from tablename
where reltnship = 'owner'
or reltnship = 'prevown'

Related

Tie-breaking mutliple matches on MAX() in SQL

I have a table that looks like this:
| client_id | program_id | provider_id | date_of_service | data_entry_date | data_entry_time |
| --------- | ---------- | ----------- | --------------- | --------------- | --------------- |
| 2 | 5 | 6 | 02/02/2022 | 02/02/2022 | 0945 |
| 2 | 5 | 6 | 02/02/2022 | 02/07/2022 | 0900 |
| 2 | 5 | 6 | 02/04/2022 | 02/04/2022 | 1000 |
| 2 | 5 | 6 | 02/04/2022 | 02/04/2022 | 1700 |
| 2 | 5 | 6 | 02/04/2022 | 02/05/2022 | 0800 |
| 2 | 5 | 6 | 02/04/2022 | 02/05/2022 | 0900 |
I need to get the most recent date_of_service entered. From the table above, the desired result/row is:
date_of_service = 02/04/2022, data_entry_date = 02/05/2022, data_entry_time = 0900
This resulting date_of_service will be left joined to the master table.
This query mostly works:
SELECT t1.client_id, t1.program_id, t1.provider_id, t2.date_of_service
FROM table1 as t1
WHERE provider_id = '6'
LEFT JOIN
(SELECT client_id, program_id, provider_id, date_of_service
FROM table2) as t2
ON t2.client_id = t1.client_id
AND t2.program_id = t1.program_id
AND t2.provider_id = t1.provider_id
AND t2.date_of_service =
(SELECT MAX(date_of_service)
FROM t2 as t3
WHERE t3.client_id = t1.client_id
AND t3.program_id = t1.program_id
AND t3.provider_id = t1.provider_id
)
)
But it also returns multiple rows whenever there is more than one match on the max(date_of_service).
To solve this, I need to use the max data_entry_date to break any ties whenever there is more than one row that matches the max(date_of_service). Likewise, I also need to use the max data_entry_time to break any ties whenever there is more than one row that also matches the max data_entry_date.
I tried the following:
SELECT t1.client_id, t1.program_id, t1.provider_id, t2.date_of_service
FROM table1 as t1
WHERE provider_id = '6'
LEFT JOIN
(SELECT TOP(1) client_id, program_id, provider_id, date_of_service, data_entry_date, data_entry_time
FROM table2
ORDER BY date_of_service DESC, data_entry_date DESC, data_entry_time DESC
) as t2
ON t2.client_id = t1.client_id
AND t2.program_id = t1.program_id
AND t2.provider_id = t1.provider_id
But I can only get it to return null values for the date_of_service.
Likewise, this:
SELECT t1.client_id, t1.program_id, t1.provider_id, t2.date_of_service
FROM table1 as t1
WHERE provider_id = '6'
LEFT JOIN
(
SELECT TOP(1) client_id AS client_id2, program_id AS program_id2, provider_id AS provider_id2, date_of_service, data_entry_date, data_entry_time
FROM table2 AS t3
JOIN
(SELECT
MAX(date_of_service) AS max_date_of_service
,MAX(data_entry_date) AS max_data_entry_date
FROM table1
WHERE date_of_service = (SELECT MAX(date_of_service) FROM table2)
) AS t4
ON t3.date_of_service = t4.max_date_of_service
AND t3.data_entry_date = t4.max_data_entry_date
ORDER BY data_entry_time
) AS t2
ON t2.client_id2 = t1.client_id
AND t2.program_id2 = t1.program_id
AND t2.provider_id2 = t1.provider_id
... works (meaning it doesn't throw any errors), but it only seems to return null values for me.
I've tried various combinations of MAX, ORDER BY, and multiple variations of JOIN's, but haven't found one that works yet.
I don't know what version my SQL database is, but it doesn't appear to handle window functions like OVER and PARTITION or other things like COALESCE. I've been using DBeaver 22.2.0 to test the SQL scripts.
Based on your what you've provided, looks like you can simply query table2:
SELECT client_id, program_id, provider_id, MAX(date_of_service), MAX(data_entry_date), MAX(data_entry_time)
FROM table2
GROUP BY client_id, program_id, provider_id
If you need to join this result set to table1, just JOIN to the statement above on client_id, program_id, provider_id
Try using below query. This is using just joins and sub query.
SELECT TOP 1 * FROM table1 t1
JOIN (
SELECT
MAX(date_of_Service) AS Max_date_of_Service
,MAX(data_entry_date) AS Max_data_entry_date
FROM table1
WHERE date_of_Service = (SELECT MAX(date_of_Service) FROM table1)
)t2
ON t1.date_of_Service = t2.Max_date_of_Service
AND t1.data_entry_date = t2.Max_data_entry_date
ORDER BY data_entry_time

SQL Joins . One to many relationship

I have two tables as below
Table 1
-----------------------------------
UserID | UserName | Age | Salary
-----------------------------------
1 | foo | 22 | 33000
-----------------------------------
Table 2
------------------------------------------------
UserID | Age | Salary | CreatedDate
------------------------------------------------
1 | NULL | 35000 | 2015-01-01
------------------------------------------------
1 | 28 | NULL | 2015-02-01
------------------------------------------------
1 | NULL | 28000 | 2015-03-01
------------------------------------------------
I need the result like this.
Result
-----------------------------------
UserID | UserName | Age | Salary
-----------------------------------
1 | foo | 28 | 28000
-----------------------------------
This is just an example. In my real project I have around 6 columns like Age and Salary in above tables.
In table 2 , each record will have only have one value i.e if Age has value then Salary will be NULL and viceversa.
UPDATE :
Table 2 has CreatedDate Column. So i want to get latest "NOTNULL" CELL Value instead of maximum value.
You can get this done using a simple MAX() and GROUP BY:
select t1.userid,t1.username, MAX(t2.Age) as Age, MAX(t2.Salary) as Salary
from table1 t1 join
table2 t2 on t1.userid=t2.userid
group by t1.userid,t1.username
Result:
userid username Age Salary
--------------------------------
1 foo 28 35000
Sample result in SQL Fiddle
Note: I'm giving you the benefit of the doubt that you know what you're doing, and you just haven't told us everything about your schema.
It looks like Table 2 is actually an "updates" table, in which each row contains a delta of changes to apply to the base entity in Table 1. In which case you can retrieve each column's data with a correlated join (technically an outer-apply) and put the results together. Something like the following:
select a.UserID, a.UserName,
coalesce(aAge.Age, a.Age),
coalesce(aSalary.Salary, a.Salary)
from [Table 1] a
outer apply (
select Age
from [Table 2] x
where x.UserID = a.UserID
and x.Age is not null
and not exists (
select 1
from [Table 2] y
where x.UserID = y.UserID
and y.Id > x.Id
and y.Age is not null
)
) aAge,
outer apply (
select Salary
from [Table 2] x
where x.UserID = a.UserID
and x.Salary is not null
and not exists (
select 1
from [Table 2] y
where x.UserID = y.UserID
and y.Id > x.Id
and y.Salary is not null
)
) aSalary
Do note I am assuming you have at minimum an Id column in Table 2 which is monotonically increasing with each insert. If you have a "change time" column, use this instead to get the latest row, as it is better.
To get the latest value based on CreatedDate, you can use ROW_NUMBER to filter for latest rows. Here the partition is based UserID and the other columns, Age and Salary.
SQL Fiddle
;WITH Cte AS(
SELECT
UserID,
Age = MAX(Age),
Salary = MAX(Salary)
FROM(
SELECT *, Rn = ROW_NUMBER() OVER(
PARTITION BY
UserID,
CASE
WHEN Age IS NOT NULL THEN 1
WHEN Salary IS NOT NULL THEN 2
END
ORDER BY CreatedDate DESC
)
FROM Table2
)t
WHERE Rn = 1
GROUP BY UserID
)
SELECT
t.UserID,
t.UserName,
Age = ISNULL(c.Age, t.Age),
Salary = ISNULL(c.Salary, t.Salary)
FROM Table1 t
LEFT JOIN Cte c
ON t.UserID = c.UserID
following query should work(working fine in MSSQL) :
select a.userID,a.username,b.age,b.sal from <table1> a
inner join
(select userID,MAX(age) age,MAX(sal) sal from <table2> group by userID) b
on a.userID=b.userID

get table join with column value

How can i join tables using column value?
I have three tables as listed below:
messages_table
-----------------------------------------------------------------------------
msg_id | msg_sub | msg_to | to_user_type | msg_from | from_user_type
-----------------------------------------------------------------------------
001 | test | 88 | manager | 42 | admin
002 | test2 | 88 | manager | 94 | manager
admin_table
-------------------------—
admin_id | admin_name
-------------------------—
001 | Super Admin
manager_table
---------------------------
manager_id | manager_name
---------------------------
88 | Mandela
94 | Kristen
How can i get the desired output as shown below with SQL query. I.e.
Join tables with respect to column values when the following criteria is met:
If user_type = admin then it should join with admin_table.
If user_type = manager then it should join with manager_table.
Desired output:
-----------------------------------------------------
msg_id | msg_sub | msg_to_name | msg_from_name
-----------------------------------------------------
001 | test | Mandela | Super Admin
002 | test2 | Mandela | Kristen
I.e. Get the join sql query based on column value.
EDIT:
I want to fetch the datafrom sql query not form the serverside coding.
I tried this query from here, i.e. Winfred's Idea ( Answered )
However, I could not understand it.
msg_by_usertype is the column based, where the value manager then it should select manager_table and if it is admin the to admin_table
As far as I understood your question, you can try this:
SELECT msg_id,
msg_body,
usersBy.userName AS msg_by,
usersTo.userName AS msg_to,
msg_by_usertype
FROM messages
INNER JOIN
(SELECT admin_id As id, admin_name as userName
FROM admin_table
UNION
SELECT manager_id As id, manager_name as userName
FROM manager_table ) usersTo ON msg_to = usersTo.id
INNER JOIN
(SELECT admin_id As id, admin_name as userName
FROM admin_table
UNION
SELECT manager_id As id, manager_name as userName
FROM manager_table ) usersBy ON msg_by = usersBy.id
Here is an SQL Fiddle to see how it works. (It only works if you cant have an admin who has the same id like a manager. Id should be unique in both tables.)
Please use the below SQL
SELECT msg_id,
msg_body,
usersBy.userName AS msg_by,
usersTo.userName AS msg_to,
msg_by_usertype
FROM messages
INNER JOIN
(SELECT admin_id As id, admin_name as userName,'admin' as usertype
FROM admin_table
UNION
SELECT manager_id As id, manager_name as userName,'manager' as usertype
FROM manager_table ) usersTo
ON msg_to = usersTo.id and msg_by_usertype = usersTo.usertype
if I understand your question correctly, you want a result like this?
MSG_ID MSG_BODY MSG_TO BY MSG_BY_USERTYPE
---------- ---------- ---------- ----------- ---------------
001 test adm1 managone manager
002 sadff adm1 adm3? admin
If so, you could use this
SELECT MSG_ID, MSG_BODY, MSG_TO,
CASE
WHEN MSG_BY_USERTYPE = 'admin' THEN COALESCE(
(SELECT ADMIN_NAME FROM ADMIN_TABLE
WHERE MSG_BY = ADMIN_ID), RTRIM(MSG_BY) CONCAT '?')
WHEN MSG_BY_USERTYPE = 'manager' THEN COALESCE(
(SELECT MANAGER_NAME FROM MANAGER_TABLE
WHERE MSG_BY = MANAGER_ID), RTRIM(MSG_BY) CONCAT '?')
ELSE ' '
END AS BY,
MSG_BY_USERTYPE
FROM MESSAGES

How do I group and order with sql at the same time?

I have got the following sqlite3 table:
Name | LastUpdated | Status
============================
Adam | 2011-05-28 | 1
Bob | 2011-05-05 | 6
Adam | 2011-05-27 | 2
Adam | 2011-05-16 | 1
Adam | 2011-05-26 | 3
Bob | 2011-05-18 | 1
Adam | 2011-05-29 | 6
and I want to select the a row per Name ordered by the LastUpdated column. So I want to get this data:
Adam | 2011-05-29 | 6
Bob | 2011-05-18 | 1
I think I have to do a subquery, but I can't figure out how to go about it.
SQLite (and MySQL) support:
SELECT t.name,
MAX(t.lastupdated),
t.status
FROM [table] t
GROUP BY t.name
But most other databases would require you to use:
SELECT a.name, a.lastupdate, a.status
FROM YOUR_TABLE a
JOIN (SELECT t.name, MAX(t.lastupdated) AS max_lastupdated
FROM YOUR_TABLE t
GROUP BY t.name) b ON b.name = a.name
AND b.max_lastupdated = a.lastupdated
...though this will return duplicates if a name has more than one record with the same highest date value.
You could do it as a self-join. In this case, I've called the table "table," substitute your own table name in:
SELECT
test.Name,
test.LastUpdated,
test.Status
FROM
test INNER JOIN
( SELECT
Name,
MAX(LastUpdated) AS LatestUpdated
FROM
test
GROUP BY
Name ) AS latest
ON test.Name = latest.name AND test.LastUpdated = latest.LatestUpdated;
Hope this helps!
SELECT
t.Name,
(Select LastUpdated from [table] t1 where t.name = t1.name order by lastUpdated desc LIMIT 1) as LastUpdated,
(Select Status from [table] where t1.name = t.name order by lastUpdated desc LIMIT 1) as Status
FROM [table] t
GROUP by Name

Joining tables based on the maximum value

Here's a simplified example of what I'm talking about:
Table: students exam_results
_____________ ____________________________________
| id | name | | id | student_id | score | date |
|----+------| |----+------------+-------+--------|
| 1 | Jim | | 1 | 1 | 73 | 8/1/09 |
| 2 | Joe | | 2 | 1 | 67 | 9/2/09 |
| 3 | Jay | | 3 | 1 | 93 | 1/3/09 |
|____|______| | 4 | 2 | 27 | 4/9/09 |
| 5 | 2 | 17 | 8/9/09 |
| 6 | 3 | 100 | 1/6/09 |
|____|____________|_______|________|
Assume, for the sake of this question, that every student has at least one exam result recorded.
How would you select each student along with their highest score? Edit: ...AND the other fields in that record?
Expected output:
_________________________
| name | score | date |
|------+-------|--------|
| Jim | 93 | 1/3/09 |
| Joe | 27 | 4/9/09 |
| Jay | 100 | 1/6/09 |
|______|_______|________|
Answers using all types of DBMS are welcome.
Answering the EDITED question (i.e. to get associated columns as well).
In Sql Server 2005+, the best approach would be to use a ranking/window function in conjunction with a CTE, like this:
with exam_data as
(
select r.student_id, r.score, r.date,
row_number() over(partition by r.student_id order by r.score desc) as rn
from exam_results r
)
select s.name, d.score, d.date, d.student_id
from students s
join exam_data d
on s.id = d.student_id
where d.rn = 1;
For an ANSI-SQL compliant solution, a subquery and self-join will work, like this:
select s.name, r.student_id, r.score, r.date
from (
select r.student_id, max(r.score) as max_score
from exam_results r
group by r.student_id
) d
join exam_results r
on r.student_id = d.student_id
and r.score = d.max_score
join students s
on s.id = r.student_id;
This last one assumes there aren't duplicate student_id/max_score combinations, if there are and/or you want to plan to de-duplicate them, you'll need to use another subquery to join to with something deterministic to decide which record to pull. For example, assuming you can't have multiple records for a given student with the same date, if you wanted to break a tie based on the most recent max_score, you'd do something like the following:
select s.name, r3.student_id, r3.score, r3.date, r3.other_column_a, ...
from (
select r2.student_id, r2.score as max_score, max(r2.date) as max_score_max_date
from (
select r1.student_id, max(r1.score) as max_score
from exam_results r1
group by r1.student_id
) d
join exam_results r2
on r2.student_id = d.student_id
and r2.score = d.max_score
group by r2.student_id, r2.score
) r
join exam_results r3
on r3.student_id = r.student_id
and r3.score = r.max_score
and r3.date = r.max_score_max_date
join students s
on s.id = r3.student_id;
EDIT: Added proper de-duplicating query thanks to Mark's good catch in comments
SELECT s.name,
COALESCE(MAX(er.score), 0) AS high_score
FROM STUDENTS s
LEFT JOIN EXAM_RESULTS er ON er.student_id = s.id
GROUP BY s.name
Try this,
Select student.name, max(result.score) As Score from Student
INNER JOIN
result
ON student.ID = result.student_id
GROUP BY
student.name
With Oracle's analytic functions this is easy:
SELECT DISTINCT
students.name
,FIRST_VALUE(exam_results.score)
OVER (PARTITION BY students.id
ORDER BY exam_results.score DESC) AS score
,FIRST_VALUE(exam_results.date)
OVER (PARTITION BY students.id
ORDER BY exam_results.score DESC) AS date
FROM students, exam_results
WHERE students.id = exam_results.student_id;
Select Name, T.Score, er. date
from Students S inner join
(Select Student_ID,Max(Score) as Score from Exam_Results
Group by Student_ID) T
On S.id=T.Student_ID inner join Exam_Result er
On er.Student_ID = T.Student_ID And er.Score=T.Score
Using MS SQL Server:
SELECT name, score, date FROM exam_results
JOIN students ON student_id = students.id
JOIN (SELECT DISTINCT student_id FROM exam_results) T1
ON exam_results.student_id = T1.student_id
WHERE exam_results.id = (
SELECT TOP(1) id FROM exam_results T2
WHERE exam_results.student_id = T2.student_id
ORDER BY score DESC, date ASC)
If there is a tied score, the oldest date is returned (change date ASC to date DESC to return the most recent instead).
Output:
Jim 93 2009-01-03 00:00:00.000
Joe 27 2009-04-09 00:00:00.000
Jay 100 2009-01-06 00:00:00.000
Test bed:
CREATE TABLE students(id int , name nvarchar(20) );
CREATE TABLE exam_results(id int , student_id int , score int, date datetime);
INSERT INTO students
VALUES
(1,'Jim'),(2,'Joe'),(3,'Jay')
INSERT INTO exam_results VALUES
(1, 1, 73, '8/1/09'),
(2, 1, 93, '9/2/09'),
(3, 1, 93, '1/3/09'),
(4, 2, 27, '4/9/09'),
(5, 2, 17, '8/9/09'),
(6, 3, 100, '1/6/09')
SELECT name, score, date FROM exam_results
JOIN students ON student_id = students.id
JOIN (SELECT DISTINCT student_id FROM exam_results) T1
ON exam_results.student_id = T1.student_id
WHERE exam_results.id = (
SELECT TOP(1) id FROM exam_results T2
WHERE exam_results.student_id = T2.student_id
ORDER BY score DESC, date ASC)
On MySQL, I think you can change the TOP(1) to a LIMIT 1 at the end of the statement. I have not tested this though.