I have the following table that designates employee assignments.
Null indicates current assignment:
|EmployeeNum|EmployeeAssignment|BeginDate |EndDate |
|1003 |Analyst |01/01/1990|02/04/2013|
|1002 |Coordinator |05/14/2000|06/01/2013|
|1003 |Trainer |07/28/2010|NULL |
|1004 |Janitor |08/09/2013|NULL |
|1005 |IT |09/02/2013|12/21/2013|
Another table designates time punches and paid hours:
|EmployeeNum| DatePaid |hworked|
|1003 | 05/11/2013|7.5 |
|1004 | 09/01/2013|8.25 |
|1005 | 09/15/2013|5.45 |
Is there a method where I can use SQL to compare both tables to determine which assignment each employee had when they worked?
Simple INNER JOIN
SELECT * FROM table1 t1
INNER JOIN table2 t2 ON t1.employeenum = t2.employeenum
Just SELECT whatever you want in your query
I would be inclined to use a correlated subquery for this:
select t2.*,
(select EmployeeAssignment
from table1 t1
where t1.employeenum = t2.employeenum and
t2.datepaid between t1.begin_date and coalesce(t1.end_date, getdate())
) as EmployeeAssignment
from table2 t2;
EDIT:
You can also use outer apply to do pretty much the same thing.
Actually, given your data structure, an outer join should work as well:
select t1.*, t2.EmployeeAssignment
from table1 t1 left join
table2 t2
on t1.employeenum = t2.employeenum and
t2.datepaid between t1.begin_date and coalesce(t1.end_date, getdate())
Related
I am a new learner to the sql language. My instructor gave me a task to comprehend the output of an inner join of two tables containing only one column named id and the number 1 inserted in both the tables twice.
The tables are named T1 and T2 and are visible in the db as shown below.
T1 \ T2
|ID | | ID |
|---| |----|
|1 | | 1 |
|1 | | 1 |
when i perform an inner join on these tables with the following code:
SELECT * FROM T1 INNER JOIN T2 ON T1.ID = T2.ID
I am getting the query result as:
|ID | ID_1 |
|---|------|
|1 | 1 |
|1 | 1 |
|1 | 1 |
|1 | 1 |
From my understanding the inner join retrieves distinct, non duplicate rows from both the tables, and should have just 2 rows in the output. why am i getting 4 rows? Please help me understand.
My apologies if this question is too basic and not upto the site's standards.
From my understanding the inner join retrieves distinct, non duplicate rows from both the tables
Simple. Your understanding is wrong.
JOIN has no relationship to "distinct" or "non-duplicate" rows. It simply produces all combinations of rows from the two tables that match the ON conditions (plus additional rows for an outer join).
Perhaps you are confusing JOIN with UNION -- which does remove duplicates. That said, I strongly recommend UNION ALL in most circumstances, because that does not incur the overhead of removing duplicates.
Maybe something like this is what you need:
select distinct T.rn, T.id, T2.id
from (select distinct ROW_NUMBER() OVER (ORDER BY T1.id) rn, T1.id from T1) T
join T2 on T.id = T2.id
order by T.rn;
Table 1:
| account_no | **other columns**...
+------------+-----------------------
| 1 |
| 2 |
| 3 |
| 4 |
Table 2:
| account_no | TX_No | Balance | History |
+------------+-------+---------+------------+
| 1 | 123 | 123 | 12.01.2011 |
| 1 | 234 | 2312 | 01.03.2011 |
| 3 | 232 | 212 | 19.02.2011 |
| 4 | 117 | 234 | 24.01.2011 |
I have multiple join query, one of the tables(Table 2) inside a query is problematic as it is a view which computes many other things, that is why each query to that table is costly. From Table 2, for each account_no in Table 1 I need the whole row with the greatest TX_NO, this is how I do it:
SELECT * FROM TABLE1 A LEFT JOIN
( SELECT
X.ACCOUNT_NO,
HISTORY,
X.BALANCE
FROM TABLE2 X INNER JOIN
(SELECT
ACCOUNT_NO,
MAX(TX_NO) AS TX_NO
FROM TABLE2
GROUP BY ACCOUNT_NO) Y ON X.ACCOUNT_NO = Y.ACCOUNT_NO) B
ON B.ACCOUNT_NO = A.ACCOUNT_NO
As I understand at first it will make the inner join for all the rows in Table2 and after that left join needed account_no's with Table1 which is what I would like to avoid.
My question: Is there a way to find the max(TX_NO) for only those accounts that are in Table1 instead of going through all? I think it will help to increase the speed of the query.
I think you are on the right track, but I don't think that you need to, and would not myself, nest the subqueries the way you have done. Instead, if you want to get each record from table 1 and the matching max record from table 2, you can try the following:
SELECT * FROM TABLE1 t1
LEFT JOIN
(
SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY account_no ORDER BY TX_No DESC) rn
FROM TABLE2 t
) t2
ON t1.account_no = t2.account_no AND
t2.rn = 1
If you want to continue with your original approach, this is how I would do it:
SELECT *
FROM TABLE1 t1
LEFT JOIN TABLE2 t2
ON t1.account_no = t2.account_no
INNER JOIN
(
SELECT account_no, MAX(TX_No) AS max_tx_no
FROM TABLE2
GROUP BY account_no
) t3
ON t2.account_no = t3.account_no AND
t2.TX_No = t3.max_tx_no
Instead of using a window function to find the greatest record per account in TABLE2, we use a second join to a subquery instead. I would expect the window function approach to perform better than this double join approach, and once you get used to it can even easier to read.
If table1 is comparatiely less expensive then you could think of doing a left outer join first which would considerable decrease the resultset and from that pick the latest transaction id records alone
select <required columns> from
(
select f.<required_columns),row_number() over (partition by account_no order by tx_id desc ) as rn
from
(
a.*,b.tx_id,b.balance,b.History
from table1 a left outer join table2 b
on a.account_no=b.account_no
)f
)g where g.rn=1
Let's assume you have the following two SQL Server tables:
t1:
|----------------------------------------------|
|name | day_planed | day_canceled | discription|
|----------------------------------------------|
|alpha| 2015-11-02 | 2015-11-01 | some |
|alpha| 2015-11-02 | 2015-10-30 | text |
|beta | 2015-11-02 | 2015-11-01 | here |
|----------------------------------------------|
t2:
|----------------------------------------------|
|name | day_planed | day_canceled | discription|
|----------------------------------------------|
|alpha| 2015-11-02 | 2015-10-30 | text |
|----------------------------------------------|
In an query of t1 I now want every entry except those of t2. I already tried something similar to
SELECT *
FROM t1 AS A
LEFT JOIN t2 as B ON (A.name = B.name
AND A.day_planed = B.day_planed
AND A.day_canceled != B.day_canceled)
Unfortunately I don't get it why it does not exclude the row from t2 in the query of t1.
A second question would be, if there is actual an easy way to make a query of t1 without t2 by just returning the row with the maximum description. I tried looking into it in SQL Server, but could only find the first identifier, which does not work for this "wonderful" implementation of sql...
There are a few ways to do it in this "wonderful" implementation.
SELECT * FROM t1
EXCEPT
SELECT * FROM t2
is one. Another is:
SELECT *
FROM t1
WHERE NOT EXISTS
( SELECT *
FROM t2
WHERE t2.name = t1.name
AND t2.day_planed = t1.day_planed
AND t2.day_canceled = t1.day_canceled
)
Or you could use LEFT JOIN and check for rows that didn't match with WHERE t2.name IS NULL after your ON clause. Like what you have but with = instead of !=
SELECT t1.*
FROM t1
LEFT JOIN t2
ON t2.name = t1.name
AND t2.day_planed = t1.day_planed
AND t2.day_canceled = t1.day_canceled
WHERE t2.name IS NULL;
If you want to check every column (including discription), go with EXCEPT.
You are just missing a where clause and a condition on discription. You also need to change != to =.
SELECT *
FROM t1 AS A
LEFT JOIN t2 as B ON (A.name = B.name
AND A.day_planed = B.day_planed
AND A.day_canceled = B.day_canceled
AND A.discription = B.discription)
WHERE B.Name IS NULL
I have two tables and I'm selecting data from the first table. If the condition is meet(Alias type = 2) and the data in second table for the id from the first table exist the I want to select the column from the second table. Here is data to explain more:
Table 1
id | Name | Location
---+--------------+---------
34 |John Smith |NewYork
36 |Mike Smith |London
45 |Bob Smith |Los Angeles
Table 2
id | Alias | Alias type
---+-------------------+-------
36 |Warren Johnson |1
36 |William Williams |2
Wanted results if alias type = 2
id |Name | Location
---+-------------------+---------
34 |John Smith |NewYork
36 |William Williams |London
45 |Bob Smith |Los Angeles
Can you help me write query to get the wanted results? Tell me if I need to explain more. I'm using a SQL Server database.
You can use a left outer join onto Table2, and then COALESCE the Alias and Name results like this:
SELECT Table1.ID, COALESCE(Table2.Alias, Table1.Name) AS Name,
Table1.Location
FROM Table1
LEFT OUTER JOIN Table2 ON Table1.ID = Table2.ID AND Table2.AliasType = 2
COALESCE works by finding the first non-null value in the supplied values.
Try something like this -- you just need to use CASE:
SELECT T1.Id,
CASE WHEN T2.Alias IS NOT NULL THEN T2.Alias ELSE T1.Name END as Name,
t1.Location
FROM Table1 T1
LEFT JOIN Table2 T2 ON T1.Id = T2.Id AND T2.AliasType = 2
Here is the SQL Fiddle.
Good luck.
The COALESCE function will let you have a fall-back expression so that the name from table 2 is used if it exists, and the name from table 1 is used otherwise. The LEFT outer join only joins table 2 when the rows exist, and t2.name is null if no record was joined.
SELECT t1.id, COALESCE(t2.name, t1.name), t1.location FROM [Table 1] as t1 LEFT JOIN [Table 2] as t2 on t1.id=t2.id and t2.[alias type]=2
So I have three tables that are involved in my problem, 2 regular tables and a join table for a has many and belongs to many relationship. They look like this:
table1
--id
--data
table2
--id
--data
table1_table2
--table1_id
--table2_id
So, my question is how I would query (using a join) for something that would have one or more values in the table1_table2 for one item in table1. For instance:
Table 1
+----------+
|id | data |
+----------+
|1 | none |
+----------+
|4 | match|
+----------+
Table 2
+----------+
|id | data |
+----------+
|1 | one |
+----------+
|2 | two |
+----------+
table1_table2
+----------------------+
|table1_id | table2_id |
+----------------------+
|1 | 1 |
+----------------------+
|4 | 1 |
+----------------------+
|4 | 2 |
+----------------------+
I need a query that would match Table 1 row id 4 because it has a link via the join to both row 1 and 2 from table 2. If this is confusing please ask anything.
Maybe I was a bit unclear, I am using table1_table2 as a join not as a from. I need to make sure it matches 1 and 2 both from table 2.
Here is my query so far...
SELECT DISTINCT table1.id,
table2.data
FROM table1
LEFT JOIN table1_table2 ON table1.id = table1_table2.table1_id
LEFT JOIN table2 ON table2.id = table1_table2.table2_id
I need a where that will make that for an entry in table 1 it matches both 1 and 2 from table 2.
The output I am looking for would be:
+---------------------+
|table1.id|table2.data|
+---------------------+
|4 |one |
+---------------------+
|4 |two |
+---------------------+
The following approach works if you can guarantee there are no duplicates in the Table1_Table2 table. Maybe you can start here and finesse it a bit. Note how the JOIN condition works -- putting the IN in the join condition works differently than if you put the IN condition in the WHERE clause.
I've used hash marks for values that you would need to have your code insert into the SQL.
SELECT Table1.id, COUNT(Table1_Table2.Table2_id)
FROM Table1
JOIN Table1_Table2 ON (Table1_Table2.Table1_id = Table1.id
AND Table1_Table2.Table2_id IN (#somelist#))
GROUP BY Table1.id
HAVING COUNT(Table1_Table2.Table2_id) = (#length of somelist#)
Oops -- you've changed your question in the way I suggested and I've ignored your edit. But this should get you started, as it returns all the Table1 id's that you are interested in.
Here's my solution. It's not very generalized. You should post a comment if this solution doesn't work because it is too specific to your situation.
SELECT DISTINCT Table1.id
FROM Table1
INNER JOIN table1_table2 a ON (table1.id = table1_table2.table1_id
AND table2.id = 1)
INNER JOIN table1_table2 b ON (table1.id = table1_table2.table1_id
AND table2.id = 2)
If I were doing this in SQL Server I would put the values you want to check into a temp table (or table variable) and then use a variation of what #ChrisCunningham said.
CREATE TABLE #temp (Id INT)
INSERT INTO #temp
VALUES (1, 3, 7)
SELECT a.id AS table1ID, table2.data
FROM
(
SELECT Table1.id, Table1_Table2.table2_id
FROM Table1 JOIN Table1_Table2
ON (Table1_Table2.Table1_id = Table1.id
AND Table1_Table2.Table2_id IN (SELECT Id FROM #temp))
GROUP BY Table1.id
HAVING COUNT(Table1_Table2.Table2_id) = (SELECT count (*) FROM #temp)
) a
JOIN Table2 ON Table2.id = a.Table2_id
Of course I'm not sure what mechanisms Postgre has for temp tables, but you would even make it a stored proc where you use some sort of split function to create the values for the temp table rather that the way I did it. But at least this might give you an idea.
I think you should be able to select this:
select table1_id, count(*) as count from table1_table2 group by table1_id having count > 2;
group by, count and having are your friends!