Join across many same tables - sql

Theese are my tables:
users
| id | name |
|----|------|
| 1 | Bob |
| 2 | Adam |
products
| id | Name |
|----|----------|
| 1 | Keyboard |
| 2 | Mouse |
orders
| user_id | product_id |
|---------|------------|
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
I want to query all Bob orders. I can use that:
select
user_id,
product_id
from
orders o
inner join users u on u.id = o.user_id
inner join products p on p.id = o.product_id
where
o.user_id = 1
Now, for performance reasons, multiple orders tables has been added:
orders
| user_id | product_id |
|---------|------------|
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
orders_2
| user_id | product_id |
|---------|------------|
| 1 | 4 |
| 6 | 2 |
| 1 | 7 |
orders_3
| user_id | product_id |
|---------|------------|
| 1 | 8 |
| 2 | 2 |
| 4 | 1 |
How can I get all Bob's orders now? Every order_x has the same design as order. Is it possible to join them all in 1 query?

You can filter and union all orders together and then join with product.
select
user_id,
product_id
from
(
select * from orders o1 where o1.user_id = 1
union all
select * from orders_2 o2 where o2.user_id = 1
union all
select * from orders_3 o3 where o3.user_id = 1
) AS o
inner join users u on u.id = o.user_id
inner join products p on p.id = o.product_id

Related

Limit join with pagination

These are my db tables:
Users
| id | nme |
|----|------|
| 1 | Adam |
| 2 | Bob |
| 3 | Jan |
| 4 | Nico |
Products
| id | price |
|----|-------|
| 1 | 500 |
| 2 | 700 |
| 3 | 900 |
Orders
| id | user_id | product_id |
|----|---------|------------|
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 7 | 3 | 1 |
| 8 | 3 | 2 |
| 9 | 3 | 3 |
| 10 | 4 | 3 |
I want to get up to 2 users, and their products bought. I came with this:
SELECT
users.id AS 'user_id', products.id AS 'product_id'
FROM
users
INNER JOIN
orders ON orders.user_id = users.id
INNER JOIN
products ON products.id = orders.product_id
ORDER BY
orders.id
OFFSET 0 ROWS FETCH NEXT 2 ROWS ONLY
But this returns:
| user_id | product_id |
|---------|------------|
| 1 | 1 |
| 1 | 2 |
What I want to get is up to 2 users, not orders. I want to get this:
| user_id | product_id |
|---------|------------|
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
Any ideas?
I think you don't need a JOIN among tables but only using [Orders] table along with a window function such as DENSE_RANK() as seems the most suitable to filter out to return the result set such as
;WITH cte AS
(
SELECT *,
DENSE_RANK() OVER (ORDER BY [user_id]) AS rn
FROM [Orders]
)
SELECT [user_id], [product_id]
FROM cte
WHERE rn <= 2 -- returns only two user after sorted by their ids
ORDER BY 1, 2
Demo
You may use DENSE_RANK function with your join query as the following:
SELECT user_id, product_id
FROM
(
SELECT U.id AS 'user_id',
P.id AS 'product_id',
DENSE_RANK() OVER (ORDER BY U.id) dr
FROM users U INNER JOIN orders O
ON O.user_id = U.id
INNER JOIN products P
ON P.id = O.product_id
) T
WHERE dr between 1 and 2
ORDER BY user_id, product_id
WHERE dr between 1 and 2 allows you to perform pagination according to the order of user ids, i.e if you want to get users from 3 to 5, it will be WHERE dr between 3 and 5.
Check this demo.

How do I get rental users in SQL without displaying what has already been returned

I have a 3 tables database. This is a DVD rental database.
I would like to know what DVDs are currently rented to users. I use PL/SQL.
What I can't resolve is to keep the returned DVDs from showing up, especially if the same user has taken out the same DVD again.
User Table: DVD Table: Rent table:
| ID | Name | | ID | Name | | ID | USER_ID | DVD_ID | RENT_RETURN | Rent_RETURN_DATE |
| -- | ----- | | -- | ----- | | -- | ------- | ------ | ----------- | ---------------- |
| 1 | USER1 | | 1 | DVD1 | | 1 | 1 | 1 | -1 | 2020.01.01 |
| 2 | USER2 | | 2 | DVD2 | | 2 | 1 | 1 | 1 | 2020.02.01 |
| 3 | USER3 | | 3 | DVD3 | | 3 | 1 | 1 | -1 | 2020.03.01 |
| 4 | USER4 | | 4 | DVD4 | | 4 | 1 | 2 | -1 | 2020.04.01 |
| 5 | 2 | 3 | -1 | 2020.05.01 |
| 6 | 3 | 4 | -1 | 2020.06.01 |
| 7 | 3 | 2 | -1 | 2020.07.01 |
| 8 | 3 | 4 | 1 | 2020.08.01 |
What I want to reach:
| USER_NAME | DVD_NAME | RENT_DATE |
| --------- | -------- | ---------- |
| 1 | 1 | 2020.03.01 |
| 1 | 2 | 2020.04.01 |
| 2 | 3 | 2020.05.01 |
| 3 | 2 | 2020.07.01 |
I tried this but yes it's not enough:
SELECT U.NAME, D.NAME, R.RENT_RETURN_DATE
FROM USER U, DVD D, RENT R
WHERE U.ID = R.USER_ID
AND D.ID = R.DVD_ID
AND R.RENT_RETURN = 1;
Thanks in advance for your help!
One option would be using ROW_NUMBER() analytic function such as
SELECT user_name, dvd_name, rent_return_date
FROM (SELECT ROW_NUMBER() OVER
(PARTITION BY user_id, dvd_id
ORDER BY rent_return_date DESC) AS rn,
u.name AS user_name, d.name AS dvd_name, r.*
FROM rent r
JOIN "user" u
ON r.user_id = u.id
JOIN dvd d
ON r.dvd_id = d.id
WHERE rent_return = -1)
WHERE rn = 1
or directly use aggregation
SELECT u.name AS user_name,
d.name AS dvd_name,
MAX(rent_return_date) AS rent_return_date
FROM rent r
JOIN "user" u
ON r.user_id = u.id
JOIN dvd d
ON r.dvd_id = d.id
WHERE rent_return = -1
GROUP BY u.name,d.name
SELECT user_name, dvd_name, rent_return_date
FROM (SELECT ROW_NUMBER() OVER
(PARTITION BY user_id, dvd_id
ORDER BY rent_return_date DESC) AS rn,
u.name AS user_name, d.name AS dvd_name, r.*
FROM rent r
JOIN "user" u
ON r.user_id = u.id
JOIN dvd d
ON r.dvd_id = d.id
WHERE rent_return = -1)
WHERE rn = 1

SQL join master details table

I have the following two tables in SQL server:
Product:
SELECT Id, Name From Product
x------x--------------------x
| Id | Name |
x------x--------------------x
| 1 | A |
| 2 | B |
| 3 | C |
x------x--------------------x
Order:
SELECT Id, Order_Date, QTY From Order
x------x--------------------x-------x
| Id | Order_Date | 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 would like to get a table which contains list of all products and if there is any order placed for the product.
Example:
x------x--------------------x
| Id | Has_Order |
x------x--------------------x
| 1 | 1 |
| 2 | 1 |
| 3 | 0 |
x------x--------------------x
I tried left outer join but it includes all the rows from Order table. What is the most efficient way to write this SQL query?
You can use exists in a subquery:
select p.*,
(case when exists (select 1 from orders o where o.id = p.id)
then 1 else 0
end) as order_exists_flag
from products p;
For performance, you want an index on orders(id). I would expect this to be the fastest approach.
Just another option to consider
Select ID
,Has_Order = max(Flg)
From (
Select ID,Flg = 0 From [Product]
Union All
Select Distinct ID,Flg = 1 From [Order]
) src
Group By ID

I can't get my desired SQL result

I have two tables:
Owners
+----+------+------------+
| id | name | birth_year |
+----+------+------------+
| 1 | John | 1970 |
| 2 | Jane | 1980 |
| 3 | Jack | 1990 |
| 4 | Josh | 2000 |
+----+------+------------+
Buylog
+----+----------+------------+
| id | owner_id | date |
+----+----------+------------+
| 1 | 1 | 01/01/2016 |
| 2 | 2 | 01/02/2016 |
| 3 | 2 | 01/03/2016 |
| 4 | 1 | 01/04/2016 |
+----+----------+------------+
I need to get all the info from Owners table plus the count of buys per owner:
+-----------+-------------+-------------------+--------------+
| owners.id | owners.name | owners.birth_year | buylog.Count |
+-----------+-------------+-------------------+--------------+
| 1 | John | 1970 | 2 |
| 2 | Jane | 1980 | 2 |
| 3 | Jack | 1990 | 0 |
| 4 | Josh | 2000 | 0 |
+-----------+-------------+-------------------+--------------+
I tried the below query, but that returns with error:
Select
o.id,
o.name,
o.birth_year,
Count(b.id) as Count
From
owners o
Left Outer Join
buylog b
On
b.owner_id = o.id
The error message should be pretty clear, you are missing a group by clause:
Select
o.id,
o.name,
o.birth_year,
Count(b.id) as Count
From
owners o
Left Outer Join
buylog b
On
b.owner_id = o.id
Group By o.id,
o.name,
o.birth_year
Query by HoneyBadger will do just fine, however this might perform better:
SELECT o.id
, o.name
, o.birth_year
, COALESCE(b.Count, 0) AS Count
FROM owners o
LEFT JOIN (
SELECT owner_id, COUNT(*) AS Count
FROM buylog
GROUP BY owner_id
) AS b
ON b.owner_id = o.id;
It should bring exactly the same result.
SELECT o.*,
CASE
WHEN temp.Buylog_count IS NULL THEN 0
ELSE temp.Buylog_count
END
FROM Owners o
LEFT JOIN
(
SELECT b.owner_id AS oid, COUNT(*) AS Buylog_count
FROM Buylog b
GROUP BY b.owner_id
)temp ON temp.oid = o.id

Distinct on one column with multiple results

I have a problem with my query through visual basic to access database. Lets say I have two tables as bellow.
+-------+-------------+-------------+------------+
| ID | Date1 | Date2 | CustomerID |
+-------+-------------+-------------+------------+
| 1 | 1-1-2013 | 1-1-2012 | 1 |
| 2 | 1-1-2013 | 1-1-2012 | 1 |
| 3 | 1-1-2013 | 1-1-2012 | 2 |
| 4 | 1-1-2013 | 1-1-2012 | 3 |
| 5 | 1-1-2013 | 1-1-2012 | 3 |
+-------+-------------+-------------+------------+
and
+----------+---------+
| ID | Name |
+----------+---------+
| 1 | John |
| 2 | Tina |
| 3 | Patrick |
+----------+---------+
I would like to get result with only unique numbers from Customer - ID in first table like this one bellow.
+----------+----------+-------------+------------+
| ID | Date1 | Date2 | CustomerID |
+----------+----------+-------------+------------+
| 1 | 1-1-2013 | 1-1-2012 | 1 |
| 3 | 1-1-2013 | 1-1-2012 | 2 |
| 4 | 1-1-2013 | 1-1-2012 | 3 |
+----------+----------+-------------+------------+
I've tried with this query but with no luck.
sqL = " SELECT DISTINCT Order.ID, Order.Date1, Order.Date2, Customer.Name
FROM Order (Order INNER JOIN Customer ON Order.CustomerID = Customer.ID)"
But the code does not give me the result I wanted. Can you please provide me some help with my query.
UPDATED This will do it
SELECT q.OrderID, o.Date1, o.Date2, o.CustomerID, c.Name AS CustomerName
FROM
(
(SELECT CustomerID, MIN(ID) AS OrderID
FROM [Order]
GROUP BY CustomerID
) AS q INNER JOIN [Order] AS o
ON q.OrderID = o.ID
) INNER JOIN Customer AS c
ON o.CustomerID = c.ID
Output:
OrderID | Date1 | Date2 | CustomerID | CustomerName
--------|----------|----------|------------|-------------
1 | 1/1/2013 | 1/1/2012 | 1 | John
3 | 1/1/2013 | 1/1/2013 | 2 | Tina
4 | 1/1/2013 | 1/1/2012 | 3 | Patrick
UPDATE: Based on your comments your real query might look like
SELECT q.ID, o.ID_narocila, o.datum_sprejema, o.rok_izdobave, o.status_artikla, o.status_narocila o.ID_stranke, c.naziv
FROM
(
(SELECT ID_stranke, MIN(ID) AS ID
FROM Narocilo
GROUP BY ID_stranke
) AS q INNER JOIN Narocilo AS o
ON q.ID = o.ID
) INNER JOIN Stranke AS c
ON o.ID_stranke = c.ID
From the desired result set you have shown, the following query will work:
SELECT Min(T2.ID) AS MinOfID, Min(T1.Date1) AS MinOfDate1, Min(T1.Date2) AS MinOfDate2, T1.CustomerID
FROM T2 INNER JOIN T1 ON T2.ID = T1.CustomerID
GROUP BY T1.CustomerID;