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;
Related
table name: country
id country_name
1 USA
2 GERMANY
3 RUSSIA
table name: user
id user_name points country_id
1 user1 20 1
2 user2 10 2
3 user3 11 2
Result should be country-user with maximum points and only country if no user available(3rd record),like below
country_name user_name points
USA user1 20
GERMANY user3 11
RUSSIA (null) (null)
Currently I am using below query but it is sometime taking too much time, like when i have 100000 records.
SELECT c.country_name,u.user_name,u.points FROM country c
LEFT JOIN user u on u.country_id = c.id
WHERE (u.points = (SELECT MAX(points) AS points FROM user WHERE user.id = u.id) OR u.points IS NULL)
So, is there any other way to do it more efficiently, time-wise.
Thanks already!
You can use ROW_NUMBER():
SELECT c.country_name, u.user_name, u.points
FROM country c LEFT JOIN
(SELECT u.*,
ROW_NUMBER() OVER (PARTITION BY u.country_id ORDER BY u.points DESC) as seqnum
FROM user u
WHERE u.points IS NOT NULL
) u
ON u.country_id = c.id AND u.seqnum = 1;
Note: This returns one user per country, even if there are ties for the top one. If you want all of them, use RANK() instead of ROW_NUMBER().
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
I have 3 tables:
users:
Id Login
1 John
2 Bill
3 Jim
computers:
Id Name
1 Computer1
2 Computer2
3 Computer3
4 Computer4
5 Computer5
sessions:
UserId ComputerId Minutes
1 2 47
2 1 32
1 4 15
2 5 5
1 2 7
1 1 40
2 5 31
I would like to display this resulting table:
Login Total_sess Total_min Most_freq_computer Sess_on_most_freq Min_on_most_freq
John 4 109 Computer2 2 54
Bill 3 68 Computer5 2 36
Jim - - - - -
Myself I can only cover first 3 columns with:
SELECT Login, COUNT(sessions.UserId), SUM(Minutes) FROM users
LEFT JOIN sessions
ON users.Id = sessions.UserId GROUP BY users.Id
And some kind of other columns with:
SELECT main.*
FROM (SELECT UserId, ComputerId, COUNT(*) AS cnt ,SUM(Minutes)
FROM sessions
GROUP BY UserId, ComputerId) AS main
INNER JOIN (
SELECT ComputerId, MAX(cnt) AS maxCnt FROM (
SELECT ComputerId, UserId, COUNT(*) AS cnt FROM sessions GROUP BY ComputerId, UserId
)
AS Counts GROUP BY ComputerId)
AS maxes
ON main.ComputerId = maxes.ComputerId
AND main.cnt = maxes.maxCnt
But I need to get whole resulting table in one query. I feel I'm doing something completely wrong. Need help.
Here you are:
SELECT u.login, t1.total_sess, t1.total_min, t2.mf, t2.sess_mf, t2.min_mf
FROM users u
LEFT JOIN (
SELECT userid, COUNT(minutes) AS total_sess, SUM(minutes) AS total_min
FROM sessions
GROUP BY userid
) AS t1 ON t1.userid = u.id
LEFT JOIN (
SELECT userid, name AS mf, COUNT(*) AS sess_mf, SUM(minutes) AS min_mf
FROM sessions s
JOIN computers c ON c.id = s.computerid
GROUP BY userid, computerid
HAVING COUNT(computerid) >= ALL(SELECT COUNT(*)
FROM sessions s2
WHERE s2.userid = s.userid
GROUP BY s2.computerid)
) AS t2 ON t2.userid = u.id
I'm using MySQL syntax, but it should be pretty portable.
If you need anything more, feel free to ask!
EDIT: I updated the query, the previous one was wrong :(
Can't get my head around this...
I have 3 tables like this:
Computers
---------
Id
Name
ComputerLogins
--------------
Computer_Id
User_Id
NumberOfLogins
Users
-----
Id
Name
Computers "have and belong to many" Users "through" ComputerLogins.
Sample data:
Computers: Id Name
1 "Alpha"
2 "Beta"
3 "Gamma"
Users: Id Name
1 "Joe"
2 "Fred"
ComputerLogins: Computer_Id User_Id NumberOfLogins
1 1 5
1 2 12
2 1 10
2 2 6
3 1 2
3 2 4
I'm trying to construct a view that will output one row for each record in Computers, and join a Users row through MAX(NumberOfLogins) in ComputerLogins.
Desired output:
Computer_Id User_Id NumberOfLogins
1 2 12
2 1 10
3 2 4
Can you suggest a view query that will produce the desired output?
Thanks!
SELECT
CL.*, U.* --change this as needed
FROM
(
SELECT
Computer_ID, MAX(NumberOfLogins) AS NumberOfLogins
FROM
ComputerLogins
GROUP BY
Computer_ID
) maxC
JOIN
ComputerLogins CL On maxC.Computer_ID = CL.Computer_ID AND maxC.NumberOfLogins = CL.NumberOfLogins
JOIN
Users U On CL.User_ID = U.ID
Wrap in a view etc
Use:
CREATE VIEW your_view AS
SELECT c.id AS computer_id,
u.id AS user_id,
COUNT(*) AS NumberOfLogins
FROM COMPUTERS c
JOIN COMPUTERLOGINS cl ON cl.computer_id = c.id
JOIN USERS u ON u.id = cl.user_id
GROUP BY c.id, u.id
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