SQL Join - Limit to base table - mysql

I have 4 tables that I want to link together based on the information in the sales_details table which will serve as the base table (limiting results based on the sales_details table)
1)I want to pull all columns from the sales_detail table that are mastercard orders but since the sales_detail table doesn't have a column to identity the type of transaction it is, I have to:
link the sales_detail table to the sales_total table by transaction_ID to get the pay_id
use the pay_id from the sales_total table and link to payment type table in order to filter for payment type
link product_id from sales_detail table to product_detail table to get product name
BASE TABLE : sales_detail
+-------+-----------+-----------+-----------------------------------------+
| order_date | transaction_id| product_cost | product_id | country
+-------+-----------+-----------+------------------------------------------+
| 10/1 | 12345 | 20 | 87956666 | usa
| 10/1 | 12345 | 50 | 63333333 | usa
| 10/5 | 82456 | 50 | 63333333 | usa
| 10/9 | 64789 | 30 | 45665466 | canada
| 10/12 | 08546 | 19 | 78979879 | usa
| 10/15 | 87988 | 19 | 78979879 | usa
| 10/17 | 65898 | 50 | 63333333 | canada
+-------+-----------+-----------+-------------------------------------+
table : payment_type
+-------+-----------+-----------+-------------------+
| pay_id | pay_type| payment | phone_number
+-------+-----------+-----------+-------------------+
| 08585 | 24 | mastercard |214-444-1234 |
| 07895 | 35 | visa |555-111-1234 |
| 08585 | 24 | mastercard |214-444-1234 |
| 08657 | 35 | visa |817-333-1234 |
| 02345 | 24 | mastercard |214-555-1234 |
| 02245 | 35 | visa |888-555-1234 |
| 08785 | 24 | mastercard |240-555-1234 |
| 06587 | 24 | mastercard |240-555-1234 |
+-------+-----------+-----------+-------------------+
table : sales_total
+-------+-----------+-----------+----------+
| pay_id | transaction_id| unit sold |
+-------+-----------+-----------+----------+
| 08585 | 12345 | 2 |
| 07895 | 82456 | 1 |
| 08657 | 64789 | 1 |
| 04568 | 32145 | 3 |
| 02345 | 08546 | 1 |
| 08785 | 87988 | 1 |
| 06587 | 65898 | 1 |
+-------+-----------+-----------+-----------+
table : product_detail
+-------+-----------+-----------+--+
| product_name | product id
+-------+-----------+-----------+--+
popcorn | 87956666
cheetos | 63333333
soda | 93333333
milk | 45665466
| water | 78979879
+-------+-----------+-----------+--+
**I want the output to look something like this: **
IDEAL OUTPUT
+-------+-----------+-----------+--------------------------------------------+
| order_date | transaction_id| product_cost | product_id | product |
+-------+-----------+-----------+--------------------------------------------+
| 10/1 | 12345 | 20 | 87956666 | popcorn |
| 10/1 | 12345 | 50 | 63333333 | cheetos |
| 10/12 | 08546 | 19 | 78979879 | water |
| 10/15 | 87988 | 19 | 93333333 | soda |
+-------+-----------+-----------+--------------------------------------------+
im trying to get all orders from the sales_detail table that have paid by mastercard and from the usa. I tried using left joins and not only it takes forever because of the large tables (doesnt even load) and it also gives me duplicates. What am i doing wrong?
This is the code i used but failed as it took over 20+hrs and timed out:
select t1.order_date,
t1.transaction_id,
t1.product_cost,
t1.product_id
t4.product_name
from sales_detail t1
left join sales_total t2 on t1.transaction_id=t2.transaction_id
left join payment_type t3 on t2.pay_id=t3.pay_id
left join product_detail t4 on t1.product_id=t4.product_id
where t1.order_date between '2020-10-1' and'2020-12-30'
and t1.country not in ('canada')
and t3.pay_type= 24;
there is no output for this code as after 20hrs it could never finish :(
Thanks in advance! I am a beginner so still learning the ins and outs of sql! (am using hive)

There is nothing seems to be wrong with your query and the reason you failed could be due to huge data present in sales table I would presume.
How ever lets give it a try by considering few points, as You only need columns from t1 and t4 , I would go for exists rather joining the tables without taking any columns to the select clause and second point , we can do a inner join between t2 and t3 considering t2 will never have a record with value null in pay_id column
select t1.order_date,t1.transaction_id,t1.product_cost,t1.product_id,t4.product_name
from sales_detail t1
left join product_detail t4 on t1.product_id = t4.product_id
where t1.order_date between '2020-10-1' and'2020-12-30'
and t1.country not in ('canada')
and exists (select 1
from sales_total t2
join payment_type t3 on t2.pay_id = t3.pay_id
where t1.transaction_id = t2.transaction_id
and t3.pay_type= 24);
If you still face performance issue try creating index on "transaction_id" and "product_id ".
I hope this help you taking a step forward. Thanks.

Missing comma after t1.product_id.
Use country = 'usa' as the country filter instead of not in.
Check the date range.2020-10-1 is Oct of 2020 which is in future.
select
t1.order_date,
t1.transaction_id,
t1.product_cost,
t1.product_id,
t4.product_name
from sales_detail t1
join product_detail t4 on t1.product_id=t4.product_id
join sales_total t2 on t1.transaction_id=t2.transaction_id
join payment_type t3 on t2.pay_id=t3.pay_id
where t1.order_date between ('2020-12-30' and '2020-1-10') and
t1.country = 'usa' and
t3.pay_type= 24;

Related

Only display Card payments

I designed a till system about 6 years ago, and while the code is dismal and horrible to look at, it is still going and the cafe I designed it for has been using it all this time.
However, they have recently acquired a card machine, and so now when they want to view all the sales for the day they wnat to see cash vs card in two separate tables.
This is proving tricky and the SQL for it is stumping me - I can't get my head around it.
Here are the tables involved:
categories
+----+-------------------+---------+------------+
| id | name | display | ts |
+----+-------------------+---------+------------+
| 1 | Drinks | 1 | 2016-10-14 |
| 2 | General Snacks | 1 | 2016-10-14 |
| 3 | Lunch Options | 1 | 2016-10-14 |
| 4 | Conference Drinks | 1 | 2016-10-14 |
+----+-------------------+---------+------------+
products
+----+-----------------+-------+------+-----+---------+------------+
| id | name | price | cost | cID | display | ts |
+----+-----------------+-------+------+-----+---------+------------+
| 1 | English Tea | 0.6 | 0.09 | 1 | 1 | 2018-02-15 |
| 2 | Speciality Teas | 0.8 | 0.17 | 1 | 1 | 2018-02-15 |
| 3 | Crisps | 0.6 | 0.41 | 3 | 1 | 2018-02-15 |
| 4 | Chocolate Bar | 0.6 | 0.5 | 3 | 1 | 2018-02-15 |
+----+-----------------+-------+------+-----+---------+------------+
receipts
+----+-----+-----+----------+------------+
| id | oID | pID | quantity | ts |
+----+-----+-----+----------+------------+
| 1 | 1 | 26 | 1 | 2013-11-21 |
| 2 | 2 | 6 | 2 | 2013-11-21 |
| 3 | 3 | 2 | 1 | 2013-11-21 |
| 4 | 4 | 3 | 1 | 2013-11-21 |
+----+-----+-----+----------+------------+
sales
+----+-------+----------+------+------+--------+------------+
| id | total | tendered | flag | card | userID | ts |
+----+-------+----------+------+------+--------+------------+
| 1 | 1 | 1 | 0 | 0 | 4 | 2013-11-21 |
| 2 | 2 | 2 | 0 | 0 | 4 | 2013-11-21 |
| 3 | 0.6 | 0.6 | 0 | 0 | 4 | 2013-11-21 |
| 4 | 0.6 | 0.6 | 0 | 0 | 4 | 2013-11-21 |
+----+-------+----------+------+------+--------+------------+
Please bear in mind that I wrote this a long time ago and I'm aware that its not perfect. Just to explain the above, oID stands for orderID but really should be salesID and links to the sales table ID, and pID stands for productID and is a foreign key linking to the products table. Similarily, cID is really categoryID.
Okay So the cafe manager had requested a table that looks like the following:
+---------------+-----+------+-------+------------+-----------+
| Drinks | Qty | Cost | Price | Cost-Total | Qty-total |
+---------------+-----+------+-------+------------+-----------+
| Juice Carton | 2 | 33p | 60p | 66p | £1.20 |
| Filter Coffee | 11 | 20p | 80p | £2.20 | £8.80 |
| Sub Total | 13 | | | £2.86 | £10.00 |
| Grand Total | 13 | | | £2.86 | £10.00 |
+---------------+-----+------+-------+------------+-----------+
Which has been fine, I've used the following SQL statement to produce this:
SELECT
categories.name AS category, products.name, pID,
(SELECT SUM(quantity) FROM receipts s WHERE s.pID = r.pID AND DATE(s.ts) = CURDATE()) AS quantity,
products.price,
products.cost
FROM receipts r
LEFT JOIN products ON r.pID = products.id
LEFT JOIN categories ON products.cID = categories.id
WHERE DATE(r.ts) = CURDATE()
GROUP BY r.pID ORDER BY categories.name;
However now I need to display two tables, one for card and one for cash. Now the card payments are marked in the sales table with a 1 in the card column. So I tried writing this:
SELECT
categories.name AS category, products.name, pID,
(SELECT SUM(quantity) FROM receipts s WHERE s.pID = r.pID AND DATE(s.ts) = CURDATE()) AS quantity,
products.price,
products.cost
FROM receipts r
LEFT JOIN products ON r.pID = products.id
LEFT JOIN sales x on r.oID = x.id
LEFT JOIN categories ON products.cID = categories.id
WHERE DATE(r.ts) = CURDATE() AND x.card = 1
GROUP BY r.pID ORDER BY categories.name;
However it is just displaying the same data as the first query. I know where the problem is - its in the embedded SELECT statement (AS quantity), as I'm not specifying in there whether its a card payment or a cash payement. I thought that just by adding x.card = 1 a the end would do it, but evidentally not.
Can anyone help me fix this SQL problem? How can I get the card condition into the embedded SQL, as it is retrieving from receipts and receipts does not hold information as to wether its a card payment or not?
I'm lost as to how to proceed really. All help will be appreciated!
In it's basic form:
SELECT * FROM Sales WHERE card = 1
Will display just the card payments
This will give you any sales from today:
SELECT *
FROM
categories,
products,
sales,
reciepts,
LEFT JOIN
products ON reciepts.pID
sales ON reciepts.oID
categories ON products.cID
WHERE DATE(s.ts) = CURDATE()
AND sales.card = 1
`
So what you have there is correct. It's something else you are missing...
From what I can see so far you haven't selected the sales table which could be your issue.
Have you tried running the code in SQL management studio and seeing what the result is?

Get MAX inputted date within sub query

I have a script which is working but not as desired. My aim is to select the most recently inputted record on the plans database for each seller in the account_manager_sellers list.
The current issue with the script below is: It is returning the oldest record rather than the newest, for example: it is selecting a record in 2016 rather than one which has a timestamp in 2018. (eventually I need to change the WHERE clause to get all lastsale records before 2017-01-01.
Simple Database Samples.
plans AKA (sales list)
+----+------------------+-----------+
| id | plan_written | seller_id |
+----+------------------+-----------+
| 1 | 20/09/2016 09:12 | 123 |
| 2 | 22/12/2016 09:45 | 444 |
| 3 | 19/10/2016 09:07 | 555 |
| 4 | 02/10/2015 14:26 | 123 |
| 5 | 15/08/2016 11:06 | 444 |
| 6 | 16/08/2016 11:03 | 123 |
| 7 | 03/10/2016 10:15 | 555 |
| 8 | 28/09/2016 10:12 | 123 |
| 9 | 27/09/2016 15:12 | 444 |
+----+------------------+-----------+
account_manager_sellers (seller list)
+-----+----------+
| id | name |
+-----+----------+
| 123 | person 1 |
| 444 | person 2 |
| 555 | person 3 |
+-----+----------+
Current Code Used
SELECT p.plan_written, p.seller_id
FROM plans AS p NATURAL JOIN (
SELECT id, MAX(plan_written) AS lastsale
FROM plans
GROUP BY seller_id
) AS t
JOIN account_manager_sellers AS a ON a.id = p.seller_id
WHERE lastsale < "2018-05-08 00:00:00"
Summary
Using the code and example tables above, this code would return these 3 results, whilst we do expect 3 results, the MAX(plan_written) does not seem to have followed, my guess is that it is something to do with the GROUP clause, I am not sure if we can utilise an ORDER BY and LIMIT clause?
+--------------+------------------+
| seller_id | plan_written |
+--------------+------------------+
| 123 | 16/08/2016 11:03 |
| 444 | 15/08/2016 11:06 |
| 555 | 03/10/2016 10:15 |
+--------------+------------------+
The join condition in your query is off, and you should be restricting to the max date for each seller. Also, you don't need to join to the account_manager_sellers table to get your expected output:
SELECT p1.*
FROM plans p1
INNER JOIN
(
SELECT
seller_id, MAX(plan_written) AS max_plan_written
FROM plans
WHERE plan_written < '2018-05-08 00:00:00'
GROUP BY seller_id
) p2
ON p1.seller_id = p2.seller_id AND
p1.plan_written = p2.max_plan_written;

Right Outer Join from tutorial

I was reading about the right outer join from tutorialspoint. I know that when a right outer join is performed the first thing that happens is an inner join of the two tables and then any rows in the right table that are missing in the left table are given null values.
Example from tutorial:
Customers table:
+----+----------+-----+-----------+----------+
| ID | NAME | AGE | ADDRESS | SALARY |
+----+----------+-----+-----------+----------+
| 1 | Ramesh | 32 | Ahmedabad | 2000.00 |
| 2 | Khilan | 25 | Delhi | 1500.00 |
| 3 | kaushik | 23 | Kota | 2000.00 |
| 4 | Chaitali | 25 | Mumbai | 6500.00 |
| 5 | Hardik | 27 | Bhopal | 8500.00 |
| 6 | Komal | 22 | MP | 4500.00 |
| 7 | Muffy | 24 | Indore | 10000.00 |
+----+----------+-----+-----------+----------+
Orders table:
+-----+---------------------+-------------+--------+
|OID | DATE | CUSTOMER_ID | AMOUNT |
+-----+---------------------+-------------+--------+
| 102 | 2009-10-08 00:00:00 | 3 | 3000 |
| 100 | 2009-10-08 00:00:00 | 3 | 1500 |
| 101 | 2009-11-20 00:00:00 | 2 | 1560 |
| 103 | 2008-05-20 00:00:00 | 4 | 2060 |
+-----+---------------------+-------------+--------+
Query:
SELECT ID, NAME, AMOUNT, DATE
FROM CUSTOMERS
RIGHT JOIN ORDERS
ON CUSTOMERS.ID = ORDERS.CUSTOMER_ID;
Result:
+------+----------+--------+---------------------+
| ID | NAME | AMOUNT | DATE |
+------+----------+--------+---------------------+
| 3 | kaushik | 3000 | 2009-10-08 00:00:00 |
| 3 | kaushik | 1500 | 2009-10-08 00:00:00 |
| 2 | Khilan | 1560 | 2009-11-20 00:00:00 |
| 4 | Chaitali | 2060 | 2008-05-20 00:00:00 |
+------+----------+--------+---------------------+
Why is the result of the right outer join the same as the original right table? How is this right join in anyway useful? I see it as pointless.
This isn't a useful query for RIGHT JOIN. Since all orders should have a valid customer (in fact, there should be a foreign key relationship between the Order and Customers tables), there will never be an order with no matching customer, so you'll never get any null values added.
I think they included this query just to contrast it with the almost identical query on the LEFT JOIN and FULL JOIN pages of the tutorial. Those queries show all orders, as well as all customers that don't have any orders (they have NULL in the AMOUNT and DATE columns.
To get the equivalent result with RIGHT JOIN you can simply swap the order of the tables:
SELECT ID, NAME, AMOUNT, DATE
FROM ORDERS
RIGHT JOIN CUSTOMERS
ON CUSTOMERS.ID = ORDERS.CUSTOMER_ID;
Because LEFT JOIN and RIGHT JOIN are equivalent like this, most programmers just use LEFT JOIN.
This is not the right example in that case.To see something which is there in orders table but not in customers table the id should be something that is not present in customers,such as 8,9 etc.

mysql join 3 tables by id

I have 3 tables to join and need some help to make it work, this is my schema:
donations:
+--------------------+------------+
| uid | amount | date |
+---------+----------+------------+
| 1 | 20 | 2013-10-10 |
| 2 | 5 | 2013-10-03 |
| 2 | 50 | 2013-09-25 |
| 2 | 5 | 2013-10-01 |
+---------+----------+------------+
users:
+----+------------+
| id | username |
+----+------------+
| 1 | rob |
| 2 | mike |
+----+------------+
causes:
+--------------------+------------+
| id | uid | cause | <missing cid (cause id)
+---------+----------+------------+
| 1 | 1 | stop war |
| 2 | 2 | love |
| 3 | 2 | hate |
| 4 | 2 | love |
+---------+----------+------------+
Result I want (data cropped for reading purposes)
+---------+-------------+---------+-------------+
| id | username | amount | cause |
+---------+-------------+---------+-------------+
| 1 | rob | 20 | stop war |
| 2 | mike | 5 | love |
+---------+-------------+-----------------------+
etc...
This is my current query, but returns double data:
SELECT i.*, t.cause as tag_name
FROM users i
INNER JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.uid = tti.uid)
EDIT: fixed sql schema on fiddle
http://sqlfiddle.com/#!2/0e06c/1 schema and data
How I can do this?
It seems your table's model is not right. There should be a relation between the Causes and Donations.
If not when you do your joins you will get duplicated rows.
For instance. Your model could look like this:
Donations
+--------------------+------------+
| uid | amount | date | causeId
+---------+----------+------------+
| 1 | 20 | 2013-10-10 | 1
| 2 | 5 | 2013-10-03 | 2
| 2 | 50 | 2013-09-25 | 3
| 2 | 5 | 2013-10-01 | 2
+---------+----------+------------+
causes:
+----------------------+
| id | cause |
+---------+------------+
| 1 | stop war |
| 2 | love |
| 3 | hate |
+---------+------------+
And the right query then should be this
SELECT i.*, t.cause as tag_name
FROM users i
INNER JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.id = tti.causeId)
Try this
SELECT CONCAT(i.username ,' ',i.first_name) `name`,
SUM(tti.amount),
t.cause AS tag_name
FROM users i
LEFT JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.uid = tti.uid)
GROUP BY i.id
Fiddle
You need to match the id from both the users and causes table at the same time, like so:
SELECT i.*, t.cause as tag_name
FROM users i
INNER JOIN donations tti ON (tti.uid = i.id)
INNER JOIN causes t ON (t.uid = tti.uid and t.id = i.id)
Apologies for formatting, I'm typing this on a phone.

Join multiple tables and result multiple records

I have 3 tables with information.
Table1: Orders
+---------+----------------+------------+---------------+
| OrderID | OrderDate | Community | Status |
+-------------------------------------------------------+
1 | 1 march 2013 | S1 | Approved
2 | 5 march 2013 | S2 | Aporoved
3 | 7 march 2013 | Z1 | Approved
+-------------------------------------------------------+
Table2: OrderArtickles
+------------------------------------------------------------------+
|Ordertitem | OrderID | ArtikelID | UnitPrice | Delivered |
+------------------------------------------------------------------+
| 1 | 1 | 20 | 5 | yes
| 2 | 1 | 20 | 5 | yes
| 3 | 2 | 21 | 10 | yes
| 4 | 3 | 30 | 50 | yes
+-------------------------------------------------------------------+
Table3: users
+-----------------------------------------------------+
| Userid | Username | Community | Department |
+-----------------------------------------------------+
| 1 | User1 | S1 | S
| 2 | User2 | S2 | S
| 3 | User3 | Z1 | Z
+-----------------------------------------------------+
I need a MySQL query that give the following output:
+--------------------------------------+
| Department | TotalPriceOfArtikels
+--------------------------------------+
| S | 20
| Z | 50
+--------------------------------------+
I tried with JOIN, SUM, GROUP BY but without result. The problem that I have is that the one order gives multiple articles. Who can help me?
try this
select Department , sum(UnitPrice) as TotalPriceOfArtikels
from users u
inner join Orders o
on o.Community = u.Community
inner join OrderArtickles oa
on oa.OrderId = o.OrderId
group by Department
DEMO HERE
OUTPUT:
Department TotalPriceOfArtikels
S 20
Z 50
Something like this:
select us.Department,
sum ( art.UnitPrice ) as TotalPriceOfArtikels
from user us
left join orders ors
on ( us.Community = ors.Community)
left join OrderArtickles art
on ( ors.OrderID = art.OrderID)
group by us.Department
------------------EDITED------------------------
I copy the same values and structures of your tables in my mysql, and the result is fine, it give me:
Department TotalPriceOfArtikels
S 20
Z 50
Maybe you want to check a condition, like if delivered = yes or status = aprobed??....with this query the result it's the same that you have posted ;)
Saludos ;)