SQL query using (self?) Join - sql

I have a problem with a question I don't fully understand. Was wondering if anyone could help me with it, Or at least help me understand.
Lets say we have a table 'Jobs' with 2 columns job numbers 'jobnum', and employee numbers 'empnum'
Table: Jobs
---------------------------------------
| jobnum | empnum |
---------------------------------------
| 125 | 4785 |
| 100 | 4200 |
| 305 | 4001 |
| 125 | 4224 |
| 102 | 4840 |
| 100 | 4224 |
| 107 | 4534 |
| 255 | 4200 |
| 208 | 4224 |
| 301 | 4785 |
---------------------------------------
I like the job that was done at a certain work site, lets take jobnum '125', and want to know the other jobnums of the same employees. It shows that two people worked on jobnum 125. '4224' and '4785'. How would I write a SQL query that would output the jobnums of the same people that did job '125'. I am supposed to use Join query and cannot use sub-query
I understand how I would do it using a sub-query but don't know how I would going about it using a Join. I am assuming I would do a self join? Perhaps I don't fully understand Joins =/

Using Join:
Select
j1.jobnum,
j1.empnum
From jobs j1
Join jobs j2
On j1.empnum = j2.empnum
Where j2.jobnum = 125
The query says "Give me all the records from jobs (j1) that have the same empnum as any record in jobs (j2) which has a jobnum of 125. That's the self-join that you're looking for.

This is a generic solution
SELECT T1.*
JOBS T1
INNER JOIN JOBS T2
ON( T1.jobnum=T2.jobnum AND T1.empnum<> T2.empnum)

Related

Optimize a JOIN query, for multiple correspondances between tables

I am trying to optimize a function (MySQL), but the JOIN is still not completely understood (I try to make a simple example) :
I have 2 tables :
Table ITEMS
ID | ID_ORDER | ID_BOX | NAME
001 | 2564 | 123 | SPOON_1
002 | 2564 | 123 | SPOON_2
006 | 2564 | 123 | SHOES_2
007 | 2564 | 123 | SHOES_1
003 | 2565 | 124 | SPOON_1
004 | 2565 | 124 | SPOON_2
008 | 2565 | 124 | SHOES_1
009 | 2565 | 124 | SHOES_2
005 | 2565 | 125 | SPOON_1
010 | 2565 | 125 | SHOES_1
The description of objects are linked to the ITEM table by ID_CONTRACT, and NAME.(Not possible to have 2 items with same name inside the same contract).
Table DESCRIPTION :
ID_ORDER | NAME_ITEM | LENGTH | WIDTH | ....
2564 | SPOON_1 | 200 | 20 ...
2564 | SPOON_2 | 300 | 20 ...
2564 | SHOES_1 | 500 | 20 ...
2564 | SHOES_2 | 600 | 20 ...
Now, I need to know all items I have in the contract, with their description, and I use this query :
SELECT *,description.* FROM items INNER JOIN description
ON (description.ID_CONTRACT=items.ID_CONTRACT AND description.NAME_ITEM=items.NAME)
WHERE ID_CONTRACT= 2564
First, I just read it is not correct query (I need to copy all description fields by hand in query?), because ID_CONTRACT is in both tables, and sometimes it gives me mistake(sometimes not), and I read there that it is not possible to ignore duplicates.
Then I am wondering, as I make a select on ITEMS table, MySQL is looking for each line a correspondance in DESCRIPTION table?
Is there a way to optimize query (another kind of JOIN), so it will not search everytime in ITEMS table, when he meets 2 elements (or more) in ITEMS, with same ID_CONTRACT/NAME ?
select * mean select all the columns from all the tables
and in your case this is like select items.,description. so with the syntax SELECT ,description. seems you are trying to select two time the columns for table description
due the fact you have the same column name ID_CONTRACT in both table this produce an ambiguity on coumn name for the DB engine during the join.
for avoid this you simply need a full reference name in join columns name eg:
table1.col1 = table2.col1
this way the db engino know which column form each table must be use for join
SELECT items.*, description.*
FROM items
INNER JOIN description ON description.ID_CONTRACT=items.ID_CONTRACT
AND description.NAME_ITEM=items.NAME
WHERE ID_CONTRACT= 2564
for your second part of question
MySQL is looking for each line a correspondance in DESCRIPTION table?
yes.
A relation db work on sets of data and retrieve all the correspondance between the tables
if really the rows are duplicated you could retrive the distinct result using
select DISTICNT col1, col2..
tipically a select * from a join on correctly normalized data set don't produce dulicated rows (at least you value in one column differ beetween the rows)
but if some column result are not important for you and can be omittedc form the result, this case can produce a result with duplicated row and you can perform a selective select using only the column name you really need and apply the disctint clause

Query to join two tables with like operator

I have two table which has data as below
Table1
+-----+-------+-------------------------+
| ID | value | Test |
+-----+-------+-------------------------+
| 200 | 2456 | yyy xxcv zuio uio tzrue |
| 201 | 2905 | xxx tttt ssrt uio uioe |
| 203 | 34567 | zzz iii ool uiol werts |
| 204 | 2356 | xxx tttt ssrt uio wertz |
| 205 | 3478 | io ser xcv erto klop |
| 206 | 4567 | xxx tttt ssrt uio poiu |
| 207 | 234 | zzz iii ool uiol wert |
+-----+-------+-------------------------+
I would like to create where clause to get rows with like operator.
for ex.
select *
from Table1
where test like '%xxcv zuio%' Or Like '%iii ool%' OR '%xcv erto%' OR '%ssrt uio%' OR '%uio uioe%'
But my problem is that there are more than 15-20 different parameter in where clause, it can also increase latter.
So I have decided to create a new table in database lets called it Table2 with parameter as shown below.
Table2
+----+-----------+
| ID | Parameter |
+----+-----------+
| 20 | xxcv zuio |
| 21 | iii ool |
| 22 | xcv erto |
| 23 | ssrt uio |
| 24 | uio uioe |
+----+-----------+
My question is how can I join this table to give the same result like above query.
I am still learning joins so any advice will be helpful.
You can use exists:
select t1.*
from Table1 t1
where exists (select 1 from table2 t2 where t1.test like concat('%', t2.parameter, '%'))
I don't think using a join is helping in this scenario, and this kind of text searching is not a strong point for SQL Server. Consider building an elasticsearch index or using another system that supports text searching as a primary use case (SQL Server does support full text search if that feature is enabled, but I've always been warned away from it, so don't have experience with it.)
If staying within TSQL is desirable for your particular situation, then I would use the awkward "OR LIKE" syntax. What it lacks in elegance it makes up for in readability.

SQL - UNION vs NULL functions. Which is better?

I have three tables: ACCT, PERS, ORG. Each ACCT is owned by either a PERS or ORG. The PERS and ORG tables are very similar and so are all of their child tables, but all PERS and ORG data is separate.
I'm writing a query to get PERS and ORG information for each account in ACCT and I'm curious what the best method of combining the information is. Should I use a series of left joins and NULL functions to fill in the blanks, or should I write the queries separately and use UNION to combine?
I've already written separate queries for PERS ACCT's and another for ORG ACCT's and plan on using UNION. My question more pertains to best practice in the future.
I'm expecting both to give me my desired my results, but I want to find the most efficient method both in development time and run time.
EDIT: Sample Table Data
ACCT Table:
+---------+---------+--------------+-------------+
| ACCTNBR | ACCTTYP | OWNERPERSNBR | OWNERORGNBR |
+---------+---------+--------------+-------------+
| 555001 | abc | 3010 | |
| 555002 | abc | | 2255 |
| 555003 | tre | 5125 | |
| 555004 | tre | 4485 | |
| 555005 | dsa | | 6785 |
+---------+---------+--------------+-------------+
PERS Table:
+---------+--------------+---------------+----------+-------+
| PERSNBR | PHONE | STREET | CITY | STATE |
+---------+--------------+---------------+----------+-------+
| 3010 | 555-555-5555 | 1234 Main St | New York | NY |
| 5125 | 555-555-5555 | 1234 State St | New York | NY |
| 4485 | 555-555-5555 | 6542 Vine St | New York | NY |
+---------+--------------+---------------+----------+-------+
ORG Table:
+--------+--------------+--------------+----------+-------+
| ORGNBR | PHONE | STREET | CITY | STATE |
+--------+--------------+--------------+----------+-------+
| 2255 | 222-222-2222 | 1000 Main St | New York | NY |
| 6785 | 333-333-3333 | 400 4th St | New York | NY |
+--------+--------------+--------------+----------+-------+
Desired Output:
+---------+---------+--------------+-------------+--------------+---------------+----------+-------+
| ACCTNBR | ACCTTYP | OWNERPERSNBR | OWNERORGNBR | PHONE | STREET | CITY | STATE |
+---------+---------+--------------+-------------+--------------+---------------+----------+-------+
| 555001 | abc | 3010 | | 555-555-5555 | 1234 Main St | New York | NY |
| 555002 | abc | | 2255 | 222-222-2222 | 1000 Main St | New York | NY |
| 555003 | tre | 5125 | | 555-555-5555 | 1234 State St | New York | NY |
| 555004 | tre | 4485 | | 555-555-5555 | 6542 Vine St | New York | NY |
| 555005 | dsa | | 6785 | 333-333-3333 | 400 4th St | New York | NY |
+---------+---------+--------------+-------------+--------------+---------------+----------+-------+
Query Option 1: Write 2 queries and use UNION to combine them:
select a.acctnbr, a.accttyp, a.ownerpersnbr, a.ownerorgnbr, p.phone, p.street, p.city, p.state
from acct a
inner join pers p on p.persnbr = a.ownerpersnbr
UNION
select a.acctnbr, a.accttyp, a.ownerpersnbr, a.ownerorgnbr, o.phone, o.street, o.city, o.state
from acct a
inner join org o on o.orgnbr = a.ownerorgnbr
Option 2: Use NVL() or Coalesce to return a single data set:
SELECT a.acctnbr,
a.accttyp,
NVL(a.ownerpersnbr, a.ownerorgnbr) Owner,
NVL(p.phone, o.phone) Phone,
NVL(p.street, o.street) Street,
NVL(p.city, o.city) City,
NVL(p.state, o.state) State
FROM
acct a
LEFT JOIN pers p on p.persnbr = a.ownerpersnbr
LEFT JOIN org o on o.orgnbr = a.ownerorgnbr
There are way more fields in each of the 3 tables as well as many more PERS and ORG tables in my actual query. Is one way better (faster, more efficient) than another?
That depends, on what you consider "better".
Assuming, that you will always want to pull all rows from ACCT table, I'd say to go for the LEFT OUTER JOIN and no UNION. (If using UNION, then rather go for UNION ALL variant.)
EDIT: As you've already shown your queries, mine is no longer required, and did not match your structures. Removing this part.
Why LEFT JOIN? Because with UNION you'd have to go through ACCT twice, based on "parent" criteria (whether separate or done INNER JOIN criteria), while with plain LEFT OUTER JOIN you'll probably get just one pass through ACCT. In both cases, rows from "parents" will most probably be accessed based on primary keys.
As you are probably considering performance, when looking for "better", as always: Test your queries and look at the execution plans with adequate and fresh database statistics in place, as depending on the data "layout" (histograms, etc.) the "better" may be something completely different.
I think you misunderstand what a Union does versus a join statement. A union takes the records from multiple tables, generally similar or the same structure and combines them into a single resultset. It is not meant to combine multiple dissimilar tables.
What I am seeing is that you have two tables PERS and ORG with some of the same data in it. In this case I suggest you union those two tables and then join to ACCT to get the sample output.
In this case to get the output as you have shown you would want to use Outer joins so that you don't drop any records without a match. That will give you nulls in some places but most of the time that is what you want. It is much easier to filter those out later.
Very rough sample code.
SELECT a.*, b.*
from Acct as a
FULL OUTER JOIN (
Select * from PERS UNION Select * from ORG
) as b
ON a.ID = b.ID

Using a table to lookup multiple IDs on one row

I have two tables I am using at work to help me gain experience in writing SQL queries. One table contains a list of Applications and has three columns -
Application_Name, Application_Contact_ID and Business_Contact_ID. I then have a separate table called Contacts with two columns - Contact_ID and Contact_Name. I am trying to write a query that will list the Application_Name and Contact_Name for both the Applications_Contact_ID and Business_Contact_ID columns instead of the ID number itself.
I understand I need to JOIN the two tables but I haven't quite figured out how to formulate the correct statement. Help Please!
APPLICATIONS TABLE:
+------------------+------------------------+---------------------+
| Application_Name | Application_Contact_ID | Business_Contact_ID |
+------------------+------------------------+---------------------+
| Adobe | 23 | 23 |
| Word | 52 | 14 |
| NotePad++ | 44 | 989 |
+------------------+------------------------+---------------------+
CONTACTS TABLE:
+------------+--------------+
| Contact_ID | Contact_Name |
+------------+--------------+
| 23 | Tim |
| 52 | John |
| 14 | Jen |
| 44 | Carl |
| 989 | Sam |
+------------+--------------+
What I am trying to get is:
+------------------+--------------------------+-----------------------+
| Application_Name | Application_Contact_Name | Business_Contact_Name |
+------------------+--------------------------+-----------------------+
| Adobe | Tim | Tim |
| Word | John | Jen |
| NotePad++ | Carl | Sam |
+------------------+--------------------------+-----------------------+
I've tried the below but it is only returning the name for one of the columns:
SELECT Application_Name, Application_Contact_ID, Business_Contact_ID, Contact_Name
FROM Applications
JOIN Contact ON Contact_ID = Application_Contact_ID
This is a pretty critical and 101 part of SQL. Consider reading this other answer on a different question, which explains the joins in more depth. The trick to your query, is that you have to join the CONTACTS table twice, which is a bit hard to visualize, because you have to go there for both the application_contact_id and business_contact_id.
There are many flavors of joins (INNER, LEFT, RIGHT, etc.), which you'll want to familiarize yourself with for the future reference. Consider reading this article at the very least: https://www.techonthenet.com/sql_server/joins.php.
SELECT t1.application_name Application_Name,
t2.contact_name Application_Contact_name,
t3.contact_name Business_Contact_name
FROM applications t1
INNER JOIN contacts ON t2 t1.Application_Contact_ID = t2.contact_id -- join contacts for appName
INNER JOIN contacts ON t3 t1.business_Contact_ID = t3.contact_id; -- join contacts for busName

considering using Cross Apply but not sure

This is my first question on stackoverflow and am looking forward to everyone's feedback and solutions.
I would put my current SQL skills at the lower end of intermediate.
Simple one for most of you: I need to write a query in an oracle SQL environment that returns all transactions after the active employees departure date.
Table looks like this:
| Name | dept | departure date |
| John | Sales | 3.12.2014 |
| David | IT | 7.27.2014 |
| Gary | Audit | 12.5.2013 |
Transaction table
| TransID | Emp Name | Amount | TransDate |
| 1 | John | 25.00 | 3.31.2014 |
| 2 | David | 30.00 | 8.13.204 |
| 3 | Gary | 15.00 | 1.1.2014 |
I'm trying to avoid a UNION ALL since the table has over 100+ employee records. On researching the use of CROSS APPLY it seemed like it could fit the situation. Any ideas are appreciated. Thanks!
Josh
You can just use a join:
select t.*
from employees e join
transactions t
on e.emp = t.emp and e.date < t.transdate;
You could write this using apply, but I think a join makes the intention more clear.