SQL join misses data from table - sql

I have 2 tables
Purchases:
PUR# EID PID CID QTY PTIME TOTAL_PRICE
--------------------------------------------------------
100001 e01 p002 c001 1 12-AUG-17 211.65
100002 e01 p003 c001 1 20-SEP-17 118.4
100003 e02 p004 c002 5 08-OCT-17 4.95
100005 e04 p007 c004 1 15-OCT-17 119.2
100006 e03 p008 c001 1 12-OCT-17 349.3
100007 e03 p006 c003 2 10-SEP-17 35.91
Products
PID NAME QOH | QOH_THRESHOLD | ORIGINAL_PRICE DISCNT_RATE
----------------------------------------------------------------------------
p001 stapler 60 20 9.99 .1
p002 TV 6 5 249 .15
p003 camera 20 5 148 .2
p004 pencil 100 10 .99 0
p005 chair 10 8 12.98 .3
I have to find
What I am able to find is people who purchases just in date and give the count but I AM NOT ABLE TO SHOW THE PRODUCT COUNT AS 0 FOR THOSE WHO DINT PURCHASED ANYTHING IN date.
I want the output to show for all products
My Query:
select count(qty) as "noc", pd.pid, pd.name
from purchases p
inner join products pd on pd.pid = p.pid
where (to_char(p.ptime, 'Mon-YYYY')='Oct-2017'
AND p.qty =p.qty

By default inner joins only output rows that have a match. To keep rows without a match, you need a left join in this case:
SELECT COUNT(qty) AS noc, PD.pid, PD.name
FROM products PD LEFT JOIN purchases P
ON (PD.pid = P.pid AND P.ptime = 'OCT-17')
GROUP BY PD.pid, PD.name
ORDER BY noc DESC;
Demo: http://sqlfiddle.com/#!9/1f8e5f/15.
Further reading: A Visual Explanation of SQL Joins.

Related

Joining multiple tables and getting MAX value in subquery PostgreSQL

I have 4 Tables in PostgreSQL with the following structure as you can see below:
"Customers"
ID | NAME
101 Max
102 Peter
103 Alex
"orders"
ID | customer_id | CREATED_AT
1 101 2022-05-12
2 101 2022-06-14
3 101 2022-07-9
4 102 2022-02-14
5 102 2022-06-18
6 103 2022-05-22
"orderEntry"
ID | order_id | product_id |
1 3 10
2 3 20
3 3 30
4 5 20
5 5 40
6 6 20
"product"
ID | min_duration
10 P10D
20 P20D
30 P30D
40 P40D
50 P50D
Firstly I need to select "orders" with the max(created_at) date for each customer this is done with the query (it works!):
SELECT c.id as customerId,
o.id as orderId,
o.created_at
FROM Customer c
INNER JOIN Orders o
ON c.id = o.customer_id
INNER JOIN
(
SELECT customer_id, MAX(created_at) Max_Date
FROM Orders
GROUP BY customer_id
) res ON o.customer_id = res.customer_id AND
o.created_at = res.Max_date
the result will look like this:
customer_id | order_id | CREATED_AT
101 3 2022-07-9
102 5 2022-06-18
103 6 2022-05-22
Secondly I need to select for each order_id from "orderEntry" Table, "products" with the max(min_duration) the result should be:
order_id | max(min_duration)
3 P30D
5 P40D
6 P20D
and then join results from 1) and 2) queries by "order_id" and the total result which I'm trying to get should look like this:
customer_name | customer_id | Order_ID | Order_CREATED_AT | Max_Duration
Max 101 3 2022-07-9 P30D
Peter 102 5 2022-06-18 P40D
Alex 103 6 2022-05-22 P20D
I'm struggling to get query for 2) and then join everything with query from 1) to get the result. Any help I would appreciate!
You could make the first query to an CTE and use that to join the rest of the queries.
Like this.
WITH CTE AS ( SELECT c.id as customerId,
o.id as orderId,
o.created_at
FROM Customer c
INNER JOIN Orders o
ON c.id = o.customer_id
INNER JOIN
(
SELECT customer_id, MAX(created_at) Max_Date
FROM Orders
GROUP BY customer_id
) res ON o.customer_id = res.customer_id AND
o.created_at = res.Max_date)
SELECT customerId,orderId,created_at,p.min_duration
FROM CTE
JOIN (SELECT "orderId", MAX("product_id") as product_id FROM "orderEntry" GROUP BY orderId) oe ON CTE.orderId = oe.orderId
JOIN "product" pr ON oe.product_id = pr."ID"

Get N most frequently bought combination product in one transaction using MSSQL

I have a data set like below:
transaction_id
store_id
product_id
product_name
1
100
p001
product_1
1
100
p002
product_2
1
100
p003
product_3
4
100
p002
product_2
4
100
p003
product_3
5
100
p002
product_2
5
100
p003
product_3
7
100
p001
product_1
7
100
p003
product_3
8
101
p002
product_2
8
101
p003
product_3
9
101
p001
product_1
9
101
p002
product_2
2
101
p001
product_1
3
101
p002
product_2
3
101
p003
product_3
6
101
p001
product_1
6
101
p002
product_2
I am trying to find a query to give output as below.
store
freq_prod_ids
count_of_transactions
100
p002, p003
3
100
p001, p003
2
101
p001, p002
2
101
p002, p003
2
This essentially should give the top 2 [N=2] frequently bought product combinations in a single transaction for each store.
Please help to have an SQL query to get this response.
You may try the following which performs a self-join before aggregating based on store and product pairs. The row_number is used to retrieve the top 2 product pairs per store.
SELECT
store_id, freq_prod_ids,count_of_transactions
FROM (
SELECT
t1.store_id,
CONCAT(t1.product_id,', ',t2.product_id) as freq_prod_ids,
COUNT(1) as count_of_transactions,
ROW_NUMBER() OVER (PARTITION BY t1.store_id ORDER BY COUNT(1) DESC) as rn
FROM my_table t1
INNER JOIN my_table t2 on t1.store_id = t2.store_id and
t1.product_id < t2.product_id and
t1.transaction_id = t2.transaction_id
GROUP BY t1.store_id,CONCAT(t1.product_id,', ',t2.product_id)
) t3 WHERE rn <=2
View working demo db fiddle
Let me know if this works for you.

Oracle left join with conditions from the target table

I have two tables Customers, Sales like below:
Customers :
id
first_name
last_name
gender
age
customer_since
1
Daniel
Black
M
34
2014-10-13
2
Erik
Brown
M
25
2015-06-10
3
Diana
Trump
F
39
2015-10-25
4
Anna
Yao
F
19
2017-02-20
5
Christian
Sanders
M
42
2018-01-31
Sales:
id
date
book_id
customer_id
quantity
amount
1
2019-09-02
2
3
1
14.99
2
2019-10-01
1
2
1
12.99
3
2019-10-01
3
4
1
15.75
I need to get all customers and their data in sales table even if they don't have records in sales table so I did left join like below :
SELECT c.id as customer_id, c.first_name, c.last_name, c.gender, c.age, c.customer_since,
s.sdate AS sales_date, s.id as sale_id
FROM customers c
LEFT JOIN sales s
ON c.id = s.customer_id
and I got this result:
CUSTOMER_ID
FIRST_NAME
LAST_NAME
GENDER
AGE
CUSTOMER_SINCE
SALES_DATE
SALE_ID
3
Diana
Trump
F
39
25-OCT-15
02-SEP-19
1
2
Erik
Brown
M
25
10-JUN-15
01-OCT-19
2
1
Daniel
Black
M
34
13-OCT-14
-
-
4
Anna
Yao
F
19
20-FEB-17
-
-
5
Christian
Sanders
M
42
31-JAN-18
-
-
which is expected my question is what if I need to get all customers that only in the source table and customers from the target table which has specific condition I run the below query
SELECT c.id as customer_id, c.first_name, c.last_name, c.gender, c.age, c.customer_since,
s.sdate AS sales_date, s.id as sale_id
FROM customers c
LEFT JOIN sales s
ON c.id = s.customer_id
where s.id=1
and I got only one record like this
CUSTOMER_ID
FIRST_NAME
LAST_NAME
GENDER
AGE
CUSTOMER_SINCE
SALES_DATE
SALE_ID
3
Diana
Trump
F
39
25-OCT-15
02-SEP-19
1
but I need this result
CUSTOMER_ID
FIRST_NAME
LAST_NAME
GENDER
AGE
CUSTOMER_SINCE
SALES_DATE
SALE_ID
3
Diana
Trump
F
39
25-OCT-15
02-SEP-19
1
1
Daniel
Black
M
34
13-OCT-14
-
-
4
Anna
Yao
F
19
20-FEB-17
-
-
5
Christian
Sanders
M
42
31-JAN-18
-
-
What should I do?
You need to move the criteria from the WHERE clause of the second query to the ON clause of the join:
SELECT c.id as customer_id, c.first_name, c.last_name, c.gender, c.age,
c.customer_since, s.sdate AS sales_date, s.id AS sale_id
FROM customers c
LEFT JOIN sales s
ON c.id = s.customer_id AND s.id = 1;
By restricting the sales ID in the WHERE clause, you filter off any non matching records. By moving this restriction to the join, all records would be retained, but NULL values would appear for all non matching sales fields.

Joins and Loops in SQL Server 2008

How to join the below tables to get result like in "Result" table:
Table: Invoice
Inv_No Fk_Rep_ID Inv_Date Inv_Amt
3000 202 10/1/2014 35
3001 194 11/1/2014 40
3002 180 15/1/2014 55
Table :Return
Return_ID FK_Rep_ID Ret_Date Ret_Amt
2000 202 17/1/2014 67
2001 194 15/1/2015 43
Table: Credit
Credit_ID FK_Rep_ID credit_Date credit_Amnt
1000 NULL 4/2/2014 60
1001 202 5/2/2014 12
Table: Debit
Debit_ID FK_Rep_ID Debit_Date Debit_Amnt
400 NULL 4/5/2014 600
4001 194 5/5/2014 110
Table:Receipt_Items
Fk_Rec_No FK_Item_No Item_Type Rec_Item_ID
7787 1000 2 1
7788 2000 1 1
7788 3000 0 2
7788 3001 0 3
7788 3002 0 4
7788 4000 3 5
7788 4001 3 6
7789 1001 2 1
Table :Sales_Rep
Rep_ID Rep_Name
180 Vinu
194 Bibin
202 Salman
Result
Fk_Rec_No Fk_Item_No Item_Type Rep_Name Item_Date Item_Amt
7787 1000 Credit NULL 4/2/2014 -60
7788 2000 Return salman 15/1/2014 -67
7788 3000 Invoice salman 10/1/2014 35
7788 3001 Invoice Bibin 11/1/2014 40
7788 3002 Invoice Vinu 12/1/2014 55
7788 4000 Debit NULL 4/5/2014 600
7788 4001 Debit Bibin 5/5/2014 110
7789 1001 Credit Salman 5/2/2014 -12
Query :
SELECT tt.*,SR.Rep_Name
FROM(SELECT
fk_receipt_no
,fk_item_no
,CASE Item_type
WHEN 0 THEN 'INVOICE'
WHEN 1 THEN 'Return'
WHEN 2 THEN 'Credit'
WHEN 3 THEN 'Debit'
END as ITEM_type,
Case Item_type when 1 then '-'+Cast(Item_Amnt as varchar(50))
when 2 then '-'+Cast(Item_Amnt as varchar(50))
else Cast(Item_Amnt as varchar(50)) End Item_Amnt
,COALESCE(R.FK_Rep_ID,C.FK_Rep_ID,I.FK_Rep_ID) as FK_Rep_ID
,COALESCE(R.Ret_Date,C.Note_Date,I.Inv_Date) as Item_Date
FROM Recp_Item RI LEFT JOIN [Return] R ON RI.FK_Item_no=R.Return_ID
LEFT JOIN Credit C ON RI.FK_Item_No=C.Note_ID
LEFT JOIN Invoice I ON RI.FK_Item_No=I.Inv_No
) tt LEFT JOIN [Sales Rep] SR ON SR.Rep_ID=tt.FK_Rep_ID
You probably should mention that FK_Item_No refers sometimes to different things, like Credit_ID. This kind of multipurpose FK almost always results in a union for each type of item it could represent.
This is pseudo code, I expect you to provide all the tedious join criterias since you should be capable of doing so.
Notice left joins are used to the Rep table since you have some null FK's
In each select aliases are used to normalize the column names such as credit_amnt => Item_Amt. Technically this is only required for the first select in the join so long as the others are in the same order, but I usually do it for all unions for readability.
Select * From
(
Select ri.Fk_Rec_No, Credit_ID as Fk_Item_No, it.Name as Item_Type,
r.Rep_Name, c.credit_Date as Item_Date, c.credit_Amnt as Item_Amt
From Credit c inner join Receipt_Items ri left join Rep r
--join with your item type table you don't show
Union
Select ri.Fk_Rec_No, Debit_ID as Fk_Item_No, it.Name as Item_Type,
r.Rep_Name, c.Debit_Date as Item_Date, d.Debit_Amnt as Item_Amt
From Debit d inner join Receipt_Items ri left join Rep r
--join with your item type table you don't show
Union
...
) as typesUnion
Order By Fk_Rec_No, Fk_Item_No

sql server 2005 wrong output

I have 3 table stock,inward,issue
Stock table's columns and data :
part_no | part_name | totalqty
10100 ciol 30
112233 abc 20
123456 coper 50
inward table :
part_no | qty
123456 10
123456 20
10100 20
112233 15
10100 25
issue table :
part_no | qty
112233 20
112233 15
123456 10
112233 25
10100 40
10100 20
my desired output :
part_no | part_name |inwardQty |issueQty
10100 coil 45 60
112233 abc 15 60
123456 coper 30 10
following is the query i have written,but not giving my desired output
select s.part_no,s.part_name,sum(i.qty) as inwardQty,sum(p.qty)as issueQty
from stock s
left join inward i on s.part_no = i.part_no
left join issue p on s.part_no = p.part_no
group by
s.part_no,s.part_name
getting following output by this query :
part_no | part_name |inwardQty |issueQty
10100 coil 90 120
112233 abc 45 60
123456 coper 30 20
The problem is that you're matching every row for inward with every row for issue, for which they're dealing with the same part. I think subqueries would be best here:
select s.part_no,s.part_name,i.qty as inwardQty,p.qty as issueQty
from stock s
left join
(select part_no,sum(qty) as qty from inward group by part_no) i on s.part_no = i.part_no
left join
(select part_no,sum(qty) as qty from issue group by part_no) p on s.part_no = p.part_no
So now, there's only one (or zero) rows to join in each of the joins, and you don't get a cartesian product.
Try this query:
SELECT
s.part_no, s.part_name,
InwardQty = (SELECT SUM(qty) FROM #inward i WHERE i.part_no = s.part_no),
IssueQty = (SELECT SUM(qty) FROM #issue p WHERE p.part_no = s.part_no)
FROM
dbo.stock s
GROUP BY
s.part_no, s.part_name
Gives me exactly your desired output.