SQL Multiple Joining with same fields - sql-server

I want to join two table but over two field that in other table. Two column from one table by different ids.
My table structure is below:
Units
+---+---------+----+
| Uid | UnitName
+---+--------------+
| 1 | Unit A |
| 2 | Unit B |
| 3 | Unit C |
+----+---------+---+
Persons
+---+---------+-------------------+------------+------+
| Pid | PerName | PerUnit | PreviousPerUnit |
+---+---------+-------------------+------------+------+
| 1 | John | 1 | 3 |
| 2 | Alice | 2 | 1 |
| 3 | Mel | 1 | 1 |
+----+---------+------------------+------------+------+
So I want to output
+---+---------+-------------------+------------+----+
| # | PerName | UnitName | PreUnitName |
+---+---------+-------------------+------------+----+
| 1 | John | Unit A | Unit C |
| 2 | Alice | Unit B | Unit A |
| 3 | Mel | Unit A | Unit A |
+----+---------+------------------+------------+----+
How can I get this output?

You need two JOINs:
SELECT
[#] = p.Pid,
Pername = p.PerName,
UnitName = u1.UnitName,
PerUnitName = u2.UnitName
FROM Persons p
INNER JOIN Units u1
ON u1.Uid = p.PerUnit
INNER JOIN Units u2
ON u2.Uid = p.PreviousPerUnit

Try this
SELECT p.PerName,perUnitTable.UnitName,PreviousUnitTable.UnitName
FROM Person p
INNER JOIN Units perUnitTable ON p.PerUnit=perUnitTable.Uid
INNER JOIN Units PreviousUnitTable ON p.PerUnit=PreviousUnitTable.Uid

Related

Results of join listed in rows vs additional columns?

I have 2 tables, with the same exact fields and fields names. i am trying to inner join them but im having some difficulty determining how i can get my results in my desired format.
I know i can do select a.customer, a.id, a.date, a.line, a.product, b.customer, b.id, b.date, b.line, b.product but instead of having my A data and B data on the same row, id like for them to be on seperate rows.
I have 2 tables, with the same exact fields and fields names, i am trying to inner join them so that unique line becomes a row.
Table A:
|customer| id | Date | line | Product|
|--------|-----|---------|------|--------|
| 445678 | 123 | 1/1/22 | 10 | 88975 |
| 853652 | 456 | 1/10/22 | 5 | 55876 |
| 845689 | 789 | 1/25/22 | 1 | 45587 |
TABLE B:
|customer| id | Date | line | Product|
|--------|-----|---------|------|--------|
| 445678 | 489 | 1/1/22 | 1 | 87574 |
| 853652 | 853 | 1/10/22 | 12 | 45678 |
| 587435 | 157 | 2/12/22 | 3 | 25896 |
DESIRED RESULTS:
|customer| id | Date | line | Product|
|--------|-----|---------|------|--------|
| 445678 | 123 | 1/1/22 | 10 | 88975 |
| 445678 | 489 | 1/1/22 | 1 | 87574 |
| 853652 | 456 | 1/10/22 | 5 | 55876 |
| 853652 | 853 | 1/10/22 | 12 | 45678 |
my query:
select a.customer, a.id, a.date, a.line, a.product
from data1 a
inner join data2 b
on a.date = b.date
and a.customer = b.customer

Left Join Not Returning Expected Results

I have a table of Vendors (Vendors):
+-----+-------------+--+
| ID | Vendor | |
+-----+-------------+--+
| 1 | ABC Company | |
| 2 | DEF Company | |
| 3 | GHI Company | |
| ... | ... | |
+-----+-------------+--+
and a table of services (AllServices):
+-----+------------+--+
| ID | Service | |
+-----+------------+--+
| 1 | Automotive | |
| 2 | Medical | |
| 3 | Financial | |
| ... | ... | |
+-----+------------+--+
and a table that links the two (VendorServices):
+-----------+-----------+
| Vendor ID | ServiceID |
+-----------+-----------+
| 1 | 1 |
| 1 | 3 |
| 3 | 2 |
| ... | ... |
+-----------+-----------+
Note that one company may provide multiple services while some companies may not provide any of the listed services.
The query results I want would be, for a given Vendor:
+------------+----------+
| Service ID | Provided |
+------------+----------+
| 1 | 0 |
| 2 | 0 |
| 3 | 1 |
| ... | ... |
+------------+----------+
Where ALL of the services are listed and the ones that the given vendor provides would have a 1 in the Provided column, otherwise a zero.
Here's what I've got so far:
SELECT
VendorServices.ServiceID,
<Some Function> AS Provided
FROM
AllServices LEFT JOIN VendorServices ON AllServices.ID = VendorServices.ServiceID
WHERE
VendorServices.VendorID = #VendorID
ORDER BY
Service
I have two unknowns:
The above query does not return every entry in the AllServices table; and
I don't know how to write the function for the Preovided column.
You need a LEFT join of AllServices to VendorServices and a case expression to get the column provided:
select s.id,
case when v.serviceid is null then 0 else 1 end provided
from AllServices s left join VendorServices v
on v.serviceid = s.id and v.vendorid = #VendorID
See the demo.

Select from 2 tables by repeating the rows from the second to each row in the first

I have a situation that I want to simplify it this way. (I'm writing this in Sql Server)
Imagine I have 2 tables as following:
Books
+---------+
| BookID |
+---------+
| 1 |
+---------+
| 2 |
+---------+
| 3 |
+---------+
BookRentals
+---------+-------------+---------------+
| BookID | RentedBy | RentalDate |
+---------+-------------+---------------+
| 1 | A | 1/1/2015 |
+---------+-------------+---------------+
| 2 | A | 2/1/2015 |
+---------+-------------+---------------+
| 1 | B | 3/1/2015 |
+---------+-------------+---------------+
| 3 | B | 4/1/2015 |
+---------+-------------+---------------+
I need to write a select statement that produces the following result:
+---------+-------------+---------------+
| BookID | RentedBy | RentalDate |
+---------+-------------+---------------+
| 1 | A | 1/1/2015 |
+---------+-------------+---------------+
| 2 | A | 2/1/2015 |
+---------+-------------+---------------+
| 3 | A | NULL |
+---------+-------------+---------------+
| 1 | B | 2/1/2015 |
+---------+-------------+---------------+
| 2 | B | NULL |
+---------+-------------+---------------+
| 3 | B | 2/1/2015 |
+---------+-------------+---------------+
Any help is really appreciated.
So you want a cross join of all books and all renters, adding a column that displays the RentalDate (if any) for a given combination of BookID and Renter?
Try this:
SELECT B.BookId, R.RentedBy, BR.RentalDate
FROM Books AS B
CROSS JOIN (SELECT DISTINCT RentedBy FROM BookRentals) AS R
LEFT JOIN BookRentals AS BR ON BR.BookId = B.BookId AND BR.RentedBy = R.RentedBy
If you already have a table containing the ID of all renters, you might want to use that, instead of the subquery (SELECT DISTINCT RentedBy FROM BookRentals).

Where to use Outer Apply

MASTER TABLE
x------x--------------------x
| Id | Name |
x------x--------------------x
| 1 | A |
| 2 | B |
| 3 | C |
x------x--------------------x
DETAILS TABLE
x------x--------------------x-------x
| Id | PERIOD | QTY |
x------x--------------------x-------x
| 1 | 2014-01-13 | 10 |
| 1 | 2014-01-11 | 15 |
| 1 | 2014-01-12 | 20 |
| 2 | 2014-01-06 | 30 |
| 2 | 2014-01-08 | 40 |
x------x--------------------x-------x
I am getting the same results when LEFT JOIN and OUTER APPLY is used.
LEFT JOIN
SELECT T1.ID,T1.NAME,T2.PERIOD,T2.QTY
FROM MASTER T1
LEFT JOIN DETAILS T2 ON T1.ID=T2.ID
OUTER APPLY
SELECT T1.ID,T1.NAME,TAB.PERIOD,TAB.QTY
FROM MASTER T1
OUTER APPLY
(
SELECT ID,PERIOD,QTY
FROM DETAILS T2
WHERE T1.ID=T2.ID
)TAB
Where should I use LEFT JOIN AND where should I use OUTER APPLY
A LEFT JOIN should be replaced with OUTER APPLY in the following situations.
1. If we want to join two tables based on TOP n results
Consider if we need to select Id and Name from Master and last two dates for each Id from Details table.
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
LEFT JOIN
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
ORDER BY CAST(PERIOD AS DATE)DESC
)D
ON M.ID=D.ID
which forms the following result
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | NULL | NULL |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
This will bring wrong results ie, it will bring only latest two dates data from Details table irrespective of Id even though we join with Id. So the proper solution is using OUTER APPLY.
SELECT M.ID,M.NAME,D.PERIOD,D.QTY
FROM MASTER M
OUTER APPLY
(
SELECT TOP 2 ID, PERIOD,QTY
FROM DETAILS D
WHERE M.ID=D.ID
ORDER BY CAST(PERIOD AS DATE)DESC
)D
Here is the working : In LEFT JOIN , TOP 2 dates will be joined to the MASTER only after executing the query inside derived table D. In OUTER APPLY, it uses joining WHERE M.ID=D.ID inside the OUTER APPLY, so that each ID in Master will be joined with TOP 2 dates which will bring the following result.
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-08 | 40 |
| 2 | B | 2014-01-06 | 30 |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
2. When we need LEFT JOIN functionality using functions.
OUTER APPLY can be used as a replacement with LEFT JOIN when we need to get result from Master table and a function.
SELECT M.ID,M.NAME,C.PERIOD,C.QTY
FROM MASTER M
OUTER APPLY dbo.FnGetQty(M.ID) C
And the function goes here.
CREATE FUNCTION FnGetQty
(
#Id INT
)
RETURNS TABLE
AS
RETURN
(
SELECT ID,PERIOD,QTY
FROM DETAILS
WHERE ID=#Id
)
which generated the following result
x------x---------x--------------x-------x
| Id | Name | PERIOD | QTY |
x------x---------x--------------x-------x
| 1 | A | 2014-01-13 | 10 |
| 1 | A | 2014-01-11 | 15 |
| 1 | A | 2014-01-12 | 20 |
| 2 | B | 2014-01-06 | 30 |
| 2 | B | 2014-01-08 | 40 |
| 3 | C | NULL | NULL |
x------x---------x--------------x-------x
3. Retain NULL values when unpivoting
Consider you have the below table
x------x-------------x--------------x
| Id | FROMDATE | TODATE |
x------x-------------x--------------x
| 1 | 2014-01-11 | 2014-01-13 |
| 1 | 2014-02-23 | 2014-02-27 |
| 2 | 2014-05-06 | 2014-05-30 |
| 3 | NULL | NULL |
x------x-------------x--------------x
When you use UNPIVOT to bring FROMDATE AND TODATE to one column, it will eliminate NULL values by default.
SELECT ID,DATES
FROM MYTABLE
UNPIVOT (DATES FOR COLS IN (FROMDATE,TODATE)) P
which generates the below result. Note that we have missed the record of Id number 3
x------x-------------x
| Id | DATES |
x------x-------------x
| 1 | 2014-01-11 |
| 1 | 2014-01-13 |
| 1 | 2014-02-23 |
| 1 | 2014-02-27 |
| 2 | 2014-05-06 |
| 2 | 2014-05-30 |
x------x-------------x
In such cases an APPLY can be used(either CROSS APPLY or OUTER APPLY, which is interchangeable).
SELECT DISTINCT ID,DATES
FROM MYTABLE
OUTER APPLY(VALUES (FROMDATE),(TODATE))
COLUMNNAMES(DATES)
which forms the following result and retains Id where its value is 3
x------x-------------x
| Id | DATES |
x------x-------------x
| 1 | 2014-01-11 |
| 1 | 2014-01-13 |
| 1 | 2014-02-23 |
| 1 | 2014-02-27 |
| 2 | 2014-05-06 |
| 2 | 2014-05-30 |
| 3 | NULL |
x------x-------------x
In your example queries the results are indeed the same.
But OUTER APPLY can do more: For each outer row you can produce an arbitrary inner result set. For example you can join the TOP 1 ORDER BY ... row. A LEFT JOIN can't do that.
The computation of the inner result set can reference outer columns (like your example did).
OUTER APPLY is strictly more powerful than LEFT JOIN. This is easy to see because each LEFT JOIN can be rewritten to an OUTER APPLY just like you did. It's syntax is more verbose, though.

Random Join in Sql Server

I have two table that called Product and ProductImage.
There is 1-n relation between two table. One product and more than one image depending on the product.
I want to create a product view and I want to get one image randomly from ProductImage table for each product.
Example Data : http://sqlfiddle.com/#!6/43c69
I want something like below.
+-----------+------+-------------+
| ProductId | Name | WebPath |
+-----------+------+-------------+
| 1 | Foo | foowebpath2 |
| 2 | Boo | boowebpath3 |
| 3 | Zoo | zoowebpath1 |
+-----------+------+-------------+
or
+-----------+------+-------------+
| ProductId | Name | WebPath |
+-----------+------+-------------+
| 1 | Foo | foowebpath1 |
| 2 | Boo | boowebpath1 |
| 3 | Zoo | zoowebpath6 |
+-----------+------+-------------+
or
+-----------+------+-------------+
| ProductId | Name | WebPath |
+-----------+------+-------------+
| 1 | Foo | foowebpath4 |
| 2 | Boo | boowebpath2 |
| 3 | Zoo | zoowebpath5 |
+-----------+------+-------------+
or etc...
It have to be different each time.
You should try this
SELECT *, (SELECT TOP 1 WebPath FROM ProductImage PI
WHERE PI.ProductId = P.ProductId order by NEWID() ) as WebPart from Product P
Check this fiddle http://sqlfiddle.com/#!6/43c69/16
WITH Image AS
(
SELECT *, RAND(ProductImageId) R
FROM ProductImage
)
SELECT p.*, i2.* FROM Product P
INNER JOIN
(SELECT ProductId, MIN(R) R
FROM Image
GROUP BY ProductId) i1 ON i1.ProductId = p.ProductId
INNER JOIN Image i2 ON i2.R = i1.R

Resources