How do I check for occurrence in two tables simultaneously - sql

I have a SQL query (oracle) that checks for both persons and firms, the problem is that you won't find a company in the user table and the other way around.
As of now I write this in two queries, but I would like to make this into one query (for example if I can get some help creating a temporay table)
I have a info table that tells me if this is a user, a company or both
The sql looks a bit like this:
Table1:
fk_id,
info1,
info2,
info3
Info_table:
fk_id,
<info if user, company or both>
User_table:
firstname,
lastname,
adress,
fk_id
Company_table:
Companyname,
adress,
fk_id
I would like to eighter 1:
Make a temporary table that looks like this:
Temptable:
fk_id,
firstname(if user or both, else empty),
lastname(if user, else companyname),
adress
or make a query like this:
select table1.info1, table1.info2, firstname, lastname, adress
from table1,
user_table,
company_table,
info_table
where table1.fk_id = user_table.fk_id (if user or both)
or table1.fk_id = company_table.fk_id (if company)
Any tips on how to solve this would be great. What is the best solution (making a temp table or to add this into the initial query)?

Use left outer join (in this response i'll use the + operator for convenience)
select table1.info1, table1.info2,
firstname,
nvl(lastname,company_name) lastname,
nvl(user_table.adress,company_table.adress) adress
from table1,
user_table,
company_table,
info_table
where
table1.fk_id=info_table.fk_id(+)
and table1.fk_id = user_table.fk_id(+) --(if user or both)
and table1.fk_id = company_table.fk_id(+) --(if company)

You can use a union:
quick example:
select firstname
,lastname as name
,'person_table' as source_table
from person_table
union
select null
,company_name
,'company_table'
from company_table;
The result will be a list of both persons and companies.

The correct way to write this query:
select t1.info1, t1.info2, ut.firstname,
coalesce(ut.lastname, ct.company_name) as lastname,
coalesce(ut.adress, ct.adress) as address
from table1 t1 left join
info_table it
on t1.fk_id = it.fk_id left join
user_table ut
on t1.fk_id = ut.fk_id and
it.info in ('both', 'user') left join
company_table ct
on t1.fk_id = ct.fk_id and
ct.info in ('both', 'company') ;
Notes:
This uses proper, explicit, standard JOIN syntax, as recommended by Oracle itself.
This only does the joins when the type specifies that the join is appropriate.
This uses COALESCE(), a standard function, rather than the bespoke NVL().
All column names are qualified.
This uses table aliases, so the query is easier to write and to read.

Related

how to do a left join with no duplicate columns?

I have to join 2 tables and the first table I'm joining consists of:
Physicians (ID, FirstName, LastName, PracticeID, SpecialtyID, Email)
and the second table I have is:
PhysicianSpecialties( SpecialtyID, SpecialtyName)
I wrote this query to join the tables together
Select *
from physicians
right join PhysicianSpecialities
on PhysicianSpecialities.SpecialtyID = Physicians.SpecialtyID
and when I left Join them the table is now
(ID, FirstName, LastName, PracticeID, SpecialtyID, Email, SpecialtyID, SpecialtyName)
how can I rewrite this so there is only one "SpecialtyID" Column?
You didn't specify the DBMS product you are using, but: in standard SQL you can join with the USING operator if the join columns have the same name in both tables.
In this case, the "duplicated" column will automatically be removed from the result.
Select *
from physicians
right join PhysicianSpecialities using (SpecialtyID)
Not all DBMS products support that though.
You need to specify the column names of both tables instead of (*)
Select a.*,b.SpecialtyName
from physicians a
right join PhysicianSpecialities b
on b.SpecialtyID = a.SpecialtyID
Use
SELECT
physicians.ID, physicians.FirstName, physicians.LastName,
physicians.PracticeID, physicians.SpecialtyID, physicians.Email,
PhysicianSpecialities.SpecialtyName
instead of SELECT *, hence your query goes like:
SELECT
physicians.ID, physicians.FirstName, physicians.LastName,
physicians.PracticeID, physicians.SpecialtyID, physicians.Email,
PhysicianSpecialities.SpecialtyName
FROM
physicians
LEFT JOIN
PhysicianSpecialities ON Physicians.SpecialtyID = PhysicianSpecialities.SpecialtyID;
I hope it will return the desired result.

SQL Server : get unique ID from one table, another table, or both

I have two tables, TA and CMI, that contain a person_ID. The ID may exist in TA, it may exist in CMI, or it may exist in both. I want a distinct list of ALL person_ID's regardless whether they are in TA, CMI, or both tables.
I also want to be able to select them where their question_ID's are the same. However, the question_id's have different column names: TA.question and CMI.sco = question_id.
EDIT:
So, if I also wanted to do the select on question as I stated earlier AND a join to the person table, it would look something like:
select ta.person_id, person_key
from ta
left join person on person.person_id = ta.person_id
where question=7033
union -- on purpose to remove duplicates
select cmi.person_id, person_key
from cmi
left join person on person.person_id = cmi.person_id
where sco=7033
You would use union:
select person_id
from ta
union -- on purpose to remove duplicates
select person_id
from cmi;
You can use this as a CTE or subquery in a query.

SQL join beween tables with if statement

Say, I have the following two tables:
Table customer:
id, salutation, forename, lastname, companyID
and a table company:
Company_id, Company_name, Company_address
and I want to have an evaluation over all users and their company (if they belong to one)
salutation, forename, lastname, companyName
that would amount basically to a very easy script:
select salutation, forename, lastname, company_name
from customer, company
where companyID=Company_id;
The trouble now is just, that companyID can be null. (A customer doesn't need to be part of a company). And since there is no companyID null entry in the company table and any customer who has no company ID listed is omitted due to the joint statement.
Of couse I could divide it into two scripts one for companyid=null and one for not null and mix them with a UNION command, but is there perhaps something like an if statement?
something like:
select salutation, forename, lastname, placeholder
from customer, company
where
if companyID=null then placeholder=null
else (companyID=Company_id and placeholder=company_name);
?
I know there is a case statement, that can check on the field's value and return something else instead, but is there a way to combine that with a joint to another table?
You are looking for an outer join:
select cu.salutation, cu.forename, cu.lastname, co.company_name
from customer cu
left join company co on cu.companyID = co.Company_id;
In general you should stop using the ancient implicit join syntax in the where clause and use an explicit JOIN operator. That is also the only cross-DBMS way to actually do an outer join (all DBMS that supported some proprietary outer join syntax have deprecated that)
Try this
select salutation, forename, lastname, placeholder
from customer, company
where
(companyID=null and placeholder=null )
OR
(companyID=Company_id and placeholder=company_name);
Use a left join instead of an inner join
select a.salutation, a.forename, a.lastname, a.company_name
from customer a
left outer join company b
on a.companyID=b.companyID;

SQL Comparing Information in Tables

I want to know how I would compare two pieces of information such as say I have two columns in my query: surname and forename. How could I check to see whether the forename exists in the surname column and visa versa?
So far I've created two temporary tables, one which selects just the surname, and one which selects all the other information. I was going to compare them by doing a join but im having no luck.
Something like:
Select u1.Surname, u1.Forename,
(select count(0) from users u2 where u1.surname = u2.forename) as CountWhereForenameEqualsMySurname,
(select count(0) from users u2 where u1.forename = u2.surname ) as CountWhereSurnameEqualsMyForename
From users u1
Without knowing the structure of your tables it is a little difficult to write a query. But I would do something like this; join the table to itself based on the surname being equal to the forename.
SELECT
t1.forename as foreName1,
t1.surname as surName1,
t2.forename as foreName2,
t2.surname as surName2
FROM
tableName as t1 INNER JOIN tablename as t2
ON
t1.surname = t2.forename

Simple SQL Problem

I have a SQL query that I cant wrap my mind around. I do not have a large amount of sql experience. So I need some help
I have a table XXX:
Social Security No (SSN).
Name.
organisation. (Finance/IT)
In english what I want is:
To select all SSNs and Names in "Finance" where there is a different name for that SSN in "IT".
My not working attempt:
select ssn , name from XXX where org = "Finance" and name not in (select name from XXX where org="IT" and ssn=the_first_ssn)
Please help.
I have decided to make it a bit more difficult.
SSN can ocur multiple times in "IT":
So I want to select all SSNs and Names in Finance where the SSN does not exist with the same Name in "IT"
You could use a subquery in an exists clause:
select ssn, name
from YourTable a
where organisation = 'Finance'
and exists
(
select *
from YourTable b
where organisation = 'IT'
and a.ssn = b.ssn
and a.name <> b.name
)
The subquery says there must be a row in IT with the same SSN but a different name.
Assuming ssn is a unique key...
select ssn, name
from XXX XXX1
where org = "Finance"
and ssn in
(
select ssn
from XXX XXX2
where org="IT"
and XXX1.name<>XXX2.name
)
SELECT distinct
t1.ssn,
t1.name
from
xxx t1
inner join xxx t2 on t1.ssn=t2.ssn and t1.name<>t2.name
where t1.org='Finance' and t2.org='IT'
I know I'm late to the party, but I'm working on learning SQL and I wanted to try my hand at a solution and compare against the existing answers. I created a table Personnel with some testing data.
My SQL Server only query uses CTEs and an INNER JOIN:
WITH
Finance AS (SELECT SSN, Name FROM Personnel WHERE Org = 'Finance'),
IT AS (SELECT SSN, Name FROM Personnel WHERE Org = 'IT')
SELECT Finance.SSN, Finance.Name
FROM Finance
INNER JOIN IT ON IT.SSN = Finance.SSN
WHERE IT.Name != Finance.Name
Alexander's solution uses a straight INNER JOIN. I rewrote it a little bit, putting the name comparison in the WHERE clause, and dropping DISTINCT because it's not required:
SELECT Finance.SSN, Finance.Name
FROM Personnel Finance
INNER JOIN Personnel IT ON Finance.SSN = IT.SSN
WHERE
(Finance.Org = 'Finance' AND IT.Org = 'IT') AND
(Finance.Name != IT.Name)
Andomar's solution using a correlated subquery inside an EXISTS clause:
SELECT SSN, Name
FROM Personnel a
WHERE
(Org = 'Finance') AND
EXISTS
(
SELECT *
FROM Personnel b
WHERE (Org = 'IT') AND (a.SSN = b.SSN) AND (a.Name != b.Name)
)
barrylloyd's solution using a correlated subquery inside an IN clause:
SELECT SSN, Name
FROM Personnel p1
WHERE
(Org = 'Finance') AND
SSN IN
(
SELECT SSN FROM Personnel p2
WHERE (Org = 'IT') AND (p1.Name != p2.Name)
)
I plugged all of these into SQL Server, and it turns out that queries 1 and 2 both generate the same query plan, and queries 3 and 4 generate the same query plan. The difference between the two groups is the former group actually does an INNER JOIN internally, while the latter group does a left semi-join instead. (See here for an explanation of the different types of joins.)
I'm assuming there is a slight performance advantage favouring the left semi-join; however, for the business case, if you want to see any data columns from the right table (for example, if you want to display both names to compare them), you would have to completely rewrite those queries to use an INNER JOIN-based solution.
So given all that, I would favour solution 2, because the performance is so similar to 3 and 4, and it's far more flexible than those as well. My solution makes the SELECT statement very easy to read, but it's more verbose than 2 and not as portable. I suppose that mine might be better for readability if you have to do additional filtering on each of the two "sub-tables," or if the results of this query are going to be used as an intermediate step to a further goal.