2

I have two tables.

common_products

  • id
  • product
  • owner_uid
  • backup_uid
  • manager_uid

ss_users

  • userID
  • firstName
  • lastName
  • email

I want to get a name/email list of all the owners, backups and managers.

I am using the query below, but was wondering if there was a more efficient way to go about querying the tables.

WORKING QUERY:

SELECT DISTINCT email,
                ( firstName + ' ' + lastName ) AS userFull,
                lastName
FROM   common_products cp
       LEFT OUTER JOIN ss_users u
                    ON u.userID = cp.owner_uid
UNION
SELECT DISTINCT email,
                ( firstName + ' ' + lastName ) AS userFull,
                lastName
FROM   common_products cp
       LEFT OUTER JOIN ss_users u
                    ON u.userID = cp.backup_uid
UNION
SELECT DISTINCT email,
                ( firstName + ' ' + lastName ) AS userFull,
                lastName
FROM   common_products cp
       LEFT OUTER JOIN ss_users u
                    ON u.userID = cp.manager_uid 

Is there a more optimized way to query the database?

4
  • It is SQL Server, isn't it? You should add a tag. Commented Dec 19, 2014 at 18:35
  • 1
    SQL is declarative. A SQL statement doesn't have performance until it is optimised and executed by a RDBMS. Which one are you using? Commented Dec 19, 2014 at 18:36
  • You could speed your query by changing the left outer join to just join (or inner join). There is no reason that you should care about NULL values in the select. Commented Dec 19, 2014 at 18:39
  • I added the tag that it is the SQL Server. I will go ahead and change the query to use just join. Commented Dec 19, 2014 at 19:23

4 Answers 4

3

I suspect that this version might be faster:

select u.email, (u.firstName+ ' '+u.lastName) AS userFull, u.lastName
from ss_users u
where exists (select 1 from common_products cp where u.userID = cp.owner_uid) or
      exists (select 1 from common_products cp where u.userID = cp.backup_uid) or
      exists (select 1 from common_products cp where u.userID = cp.manager_uid);

Then for best performance add three indexes: common_products(owner_uid), common_products(backup_uid), and common_products(manager_uid).

This will eliminate the duplicate elimination (because you are using union) and the exists should be at least as fast as the joins.

Sign up to request clarification or add additional context in comments.

5 Comments

would it be faster to have the or conditions in a single exists subquery i.e. where u.userID in (cp.owner_uid,cp.backup_uid,cp.manager_uid) or would that prevent the db from using indexes?
@FuzzyTree . . . Doubtful. The above will resolve to three index lookups. Using or (or in) in a subquery could confuse the optimize on the use of indexes.
Thank you. This is what I was looking to make. This is actually quite nice and incredibly fast. @FuzzyTree I tried your suggestion, but noticed that I was missing a few records.
@ShawnDibble that's strange because i think the query is logically equivalent. does your subquery look like this where exists (select 1 from common_products cp where u.userID in (cp.owner_uid,cp.backup_uid,cp.manager_uid))
@FuzzyTree I misunderstood your suggestion. I am getting the correct number of results now. And it appears to be as quick as Gordon's suggestion (but my data is not that large, so don't hold me to that for larger data sets).
0

I'm going to simplify it, but the JOIN is the important part. I'll leave it to you to tweak the SELECT part.

SELECT DISTINCT owner.email AS owner_email, backup.email AS back_email, manager.email AS man_email
FROM common_product cp LEFT JOIN ss_users owner on owner.userID = cp.owner_uid
LEFT JOIN ss_users backup on backup.userID = cp.backup_uid
LEFT JOIN ss_users manager on manager.userID = cp.manager_uid

1 Comment

I tried something like this, however I needed a single column of emails that I could cycle through. The query is actually built via PHP depending on if I need owner_email, backup_email, manager_email or any combination of the 3.
0

Ensure there are indexes on common_products's owner_uid, backup_uid, and manager_uid fields as well as ss_users's userID field and you could improve performance a bit further by including the columns needed on the index.

SELECT DISTINCT
    user_owner.email [OwnerEmail],user_owner.firstName + ' ' + user_owner.lastName [OwnerUserFull], user_owner.lastName [OwnerLastName],
    user_backup.email [BackupEmail],user_backup.firstName + ' ' + user_backup.lastName [BackupUserFull], user_backup.lastName [BackupLastName],
    user_manager.email [ManagerEmail],user_manager.firstName + ' ' + user_manager.lastName [ManagerUserFull], user_manager.lastName [ManagerLastName]
FROM   common_products cp
LEFT OUTER JOIN ss_users user_owner ON user_owner.userID = cp.owner_uid
LEFT OUTER JOIN ss_users user_backup ON user_backup.userID = cp.backup_uid
LEFT OUTER JOIN ss_users user_manager ON user_manager.userID = cp.manager_uid

Comments

-1

It's been a while since I've practised by SQL fu, but I think this should work:

SELECT DISTINCT email,
    (firstName+ ' '+lastName) AS userFull,
    lastName 
FROM common_products cp 
INNER JOIN  ss_users u 
ON (u.userID = cp.owner_uid OR u.userID = cp.backup_uid OR u.userID = cp.manager_uid)

2 Comments

This is more efficient?
It's more efficient in a sense that there is only one query that needs to run as opposed to 3 queries that are then union'd. Plus it's less "code" duplication and less typing. :) However, query planners in modern SQL engines (Postgres, MSSQL, Oracle, etc) are intelligent enough to figure these things out. I don't currently have an SQL set up, but the OP can simply run various queries and see how long each one runs.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.