SQL Query: joining two fields from two separate rows - sql

I have the following two tables:
USER
FID UID VALUE
4 3 John
3 3 Doe
4 4 Jack
3 4 Russel
Should be fairly clear that FID 3 = Surname, and FID 4 = Name.
DATEDATA
UID DATE
3 1234
4 4321
I want to join these two tables, so that I end up with something like this:
UID DATE NAME SURNAME
3 1234 John Doe
4 4321 Jack Russel
or... alternatively...
UID DATE FULLNAME
3 1234 John Doe
4 4321 Jack Russel
Any SQL gurus out there?
This is what I have so far:
SELECT UID, DATE, VALUE
from DATEDATA as D
left join USER as U
on U.uid = D.uid where fid = 3 OR fid = 4
But that gives me this:
UID DATE VALUE
3 1234 Doe
3 1234 John
4 4321 Russel
4 4321 Jack
Anyone?

SELECT D.UID, DATE, U.VALUE + ' ' + U2.Value as fullName
from DATEDATA as D
left join USER as U on U.uid = D.uid and U.fid = 3
left join USER as U2 on U2.uid = D.uid and U2.fid = 4
Though this could give you a NULL name whenever either first or last is NULL. You may want to use an ISNULL to make either name an empty string in that case if you can accept cases where the user would only have one name or the other in your system.

SELECT A.UID, DATEDATA.DATE, A.VALUE, B.VALUE from DATEDATA, USER A, USER B
WHERE A.UID = B.UID AND A.FID = 3 AND B.FID = 4 AND DATEDATA.UID = A.UID

select
D.UID
, D.DATE
, isnull(U.VALUE, '') 'firstName'
, isnull(U2.VALUE, '') 'surName'
from
DateData D
left join User U on U.uid = D.uid and U.fid = 3
left join User U2 on U2.uid = D.uid and U2.fid = 4

You can use something like the following. It assumes you always have a first name. If you always have some field and it's not first name, make that the first from and readjust the joins. If you're never guaranteed a value, then let us know and we'll work a harder solution.
select d.uid,COALESCE(d.date,'none entered'),COALESCE(frst.value,'') as NAME,COALESCE(lst.value,'') as SURNAME
from
user frst (NOLOCK)
left join user lst (NOLOCK) on lst.uid=frst.uid
left join datedata d (NOLOCK) on d.uid = frst.uid
where
frst.fid=4
AND lst.fid=3

Related

SQL query Postgres 12

I'm doing an inner join on a table like this:
SELECT *
FROM patient p
INNER JOIN
vaccine v
ON
p.vaccine_id = v.id
The condition f.vac_1 = mv.id might not been satisfied in the case where a person have not been vaccinated. In such case, I don't want to ignore the row, but instead of displaying the vaccine name (which is the purpose of the inner join) to display an emtpy string.
How can this be done ?
Example
Table vaccinne
id
name
1
Moderna
2
Comirnaty
3
Janssen
Table patient
id
name
vaccine_id
1
john
1
2
kermit
2
3
jessica
I'm looking for a query to produce:
id
name
vaccine_id
1
john
Moderna
2
kermit
Comirnaty
3
jessica
If I understand correctly, you want a left join starting with foo:
SELECT *
FROM foo f LEFT JOIN
vac v
ON f.vac_1 = mv.id

Sql query , grouping result from left outer join to a single cell/field of main table

Let's say that i have the following 3 tables :
IDUser Name Surname
1 Lucas Wurth
2 John Charson
3 Erik Drown
IDUser IDLocation
1 1
1 2
2 1
3 2
IDLocation Name
1 Rome
2 Milan
a User Table , a Location table , and a table to assign a location to our users.
I'd like to extract from my database all the users and have a specific column where all assigned locations are reported like so:
IDUser Name Surname Locations
1 Lucas Wurth Rome - Milan
2 John Charson Rome
3 Erik Drown Milan
Note that the user 1 has both Rome & Milan assigned
Currently my query:
SELECT
U.IDUser,
U.Name,
U.Surname,
UL.Name AS LocationName
FROM Users U
LEFT OUTER JOIN UserLocation UL ON
UL.IDUser = U.IDUser
LEFT OUTER JOIN Location L ON
UL.IDLocation = L.IDLocation
Is obviously returning a total of 4 records instead of 3 , is it possible to achievie this using the aggregate function group by? and if so how should i do this?
If you are using SqlServer you can use STRING_AGG :
SELECT
U.IDUser,
U.Name,
U.Surname,
STRING_AGG(UL.Name, ' - ') AS Locations
FROM Users U
LEFT OUTER JOIN UserLocation UL
ON UL.IDUser = U.IDUser
LEFT OUTER JOIN Location L
ON UL.IDLocation = L.IDLocation
GROUP BY U.IDUser, U.Name, U.Surname

I have 2 different table like user and user_managers,i need to get manager details based on userid

user table
empId FirstName
1 a
2 b
3 c
4 d
5 e
usermanager table
empId ManagerId
1 2
2 null
3 4
4 5
5 3
result
empid managerid firstname
1 2 b
2 null null
3 4 d
4 5 e
5 3 c
If you also want to see users which doesn't have manager you should use LEFT JOIN. If you want to see only the users that have manager previous answers should work for you.
Here is a sample :
SELECT U.empId
, UM.ManagerId
, U.FirstName
FROM User U
LEFT JOIN UserManager UM ON U.empId = UM.empId
You can use just a LEFT JOIN statement with JOIN Condition u.empID = m.ManagerId
select m.empID, m.managerID, u.FirstName
from usermanager m
left join user u on u.empID = m.ManagerId;
Demo
it's just a join
select (columns that you want)
from user u, user_manager um
where u.empId = um.empId

How to display a value if there is a value in the column and empty if there is nothing?

I want to get the value of one column if it exists and leave it empty if there is no value in that column
Table 1 (Student):
id Name dateofbirth
1 John 16-09-2015
2 Mark 25-08-2016
3 Matt 20-08-2017
4 Peter 16-08-2014
Table 2 (Relationship):
id StudentID NextOfKin Relationship Active
1 1 David Mother Y
2 2 Frank Father N
3 3 Jacob Mother Y
4 3 Park Mother N
SELECT a.Name, a.dateofbirth, b.NextOfKin
FROM dbo.Student a
LEFT OUTER JOIN dbo.Relationship b
ON a.id = b.StudentID
WHERE b.Relationship = 'Mother'
AND b.Active = 'Y'
I want to do something like if b.NextOfKin has a value enter it, else leave it blank. Please can you advice
In the above case, Peter does not have a nextOf kin, and Mark does not have a mother. I still want his name to appear but if no mother it should be blank
You can coalesce() function and put all other conditions in ON Clause instead of Where Clause
SELECT a.Name, a.dateofbirth, coalesce(b.NextOfKin,'') as NextOfKin
FROM dbo.Student a
LEFT OUTER JOIN dbo.Relationship b
ON a.id = b.StudentID
and b.Relationship = 'Mother'
AND b.Active = 'Y'

oracle 11g pivot query optimization - multiple rows to single row

I have below tables
user table
USER_ID USER_NAME
1 smith
2 clark
3 scott
4 chris
5 john
property table
P_ID PROPERTY
1 first_name
2 last_name
3 age
4 skill
user_property table
PV_ID USER_ID P_ID VALUE
1 1 1 Smith
2 1 2 A
3 1 3 34
4 1 4 Java
5 1 4 DB
6 2 1 Clark
7 2 2 B
8 2 3 39
9 2 4 Java
10 2 4 net
11 2 4 linux
12 3 1 Scott
13 3 2 C
14 3 3 31
I want to write a query which will fetch data from all above tables as below:(Skill will be first skill for that user if available otherwise null)
USER_ID USER_NAME FIRST_NAME LAST_NAME SKILL
1 smith Smith A Java
2 clark Clark B Java
3 scott Scott C null
I have tried like below but getting performance issue:
SELECT
u.user_id,
u.user_name,
MAX(DECODE(p.property, 'first_name', text_value)) firstName,
MAX(DECODE(p.property, 'last_name', text_value)) lastName,
MAX(DECODE(p.property, 'age', text_value)) age,
MAX(DECODE(p.property, 'skill', text_value)) skill
FROM user u,
property p,
user_property up,
WHERE u.user_id = up.user_id
AND p.p_id = up.p_id
GROUP BY u.user_id,
u.user_name;
How could i write this as optimized query for oracle 11g.
The performance of your query depends on the size of the table and on the indexes you have on these tables. It most cases, it is best practice to have an index on each primary and foreign key. The index on the primary key is a must anyway. The index on the foreign key speeds up joins and prevents table locks when you delete rows.
An alternative to your query would be to use more joins instead of subselects and to use the WITH clause to simplify it:
with t as (
select u.user_id, u.user_name, up.p_id, up.value
from user_property up
join user u on u.user_id = up.user_id
)
select u.user_id, u.user_name,
t_first_name.value first_name,
t_last_name.value last_name,
(select min(value) from t where t.user_id = u.user_id and t.p_id = 4) skill
from user u
left join t t_first_name on t_first_name.user_id = u.user_id and t_first_name.p_id = 1
left join t t_last_name on t_last_name.user_id = u.user_id and t_last_name.p_id = 2;
BTW: It's a data model which isn't well suited for SQL. I hope these user properties are the exception and the rest of the database has a cleaner design.
I have tried below query however getting Cartesian product.
with t as (
select u.user_id, u.user_name, up.p_id, up.value
from user_property up
join user u on u.user_id = up.user_id
where u.user_name = 'smith'
)
select u.user_id, u.user_name,
t_first_name.value first_name,
t_last_name.value last_name,
(select min(value) from t where t.user_id = u.user_id and t.p_id = 4) skill
from user u
left join t t_first_name on t_first_name.user_id = u.user_id and t_first_name.p_id = 1
left join t t_last_name on t_last_name.user_id = u.user_id and t_last_name.p_id = 2;
if i execute below query i get 5 as mentioned in my above example (As there are 5 rows in my user_property table for user_id 1 in example)
select count(u.user_id)
from user_property up
join user u on u.user_id = up.user_id
where u.user_name = 'smith'
Hence if i execute below query i get count as 3 as there are 3 rows in my use table example
with t as (
select u.user_id, u.user_name, up.p_id, up.value
from user_property up
join user u on u.user_id = up.user_id
where u.user_name = 'smith'
)
select count(u.user_id)
from user u
left join t t_first_name on t_first_name.user_id = u.user_id and t_first_name.p_id = 1
left join t t_last_name on t_last_name.user_id = u.user_id and t_last_name.p_id = 2;