need help with tricky mySQL query - mysql

I'm asking for an awful lot here - but maybe some SQL guru can show me how to extract the data I want and save me 10+ hours of google-time(tm)?
These are my tables, with only relevant fields displayed:
**event**
id
cust_id
....
.
**art**
id
art_name
...
.
**event_art**
event_id
art_id
...
.
**price**
cust_id
art_id
price
...
Prices in the "price" DB with user ID "0" is standard price, if an entry exists with art_id and cust_id that is customer specific price for that article.
What I have is cust_id and what I have for output now is just the customer specific prices with SQL:
SELECT * FROM price WHERE cust_id='{$custID}'
But I'd like to include prices for previously ordered articles, even if they do not have a customer specific price.
So what I need is to:
1 Get all id's from table event where cust_id = custID
2 Get all distinct article ID's on those orders from table event_art
3 Output "id" and "art_name" of article from "art" and "price" from price table using custID or 0 for standard price if no entry exists.
To me this sounds like a multi-line JOIN that's a bit outside my scope of SQL knowledge. Could somebody help me out, point me to a guide that deals with similar issues or... well, something?
Thanks in advance!

SELECT art_id, price
FROM price
WHERE cust_id = $cust_id
UNION ALL
SELECT art_id, price
FROM (
SELECT DISTINCT art_id
FROM event e
JOIN event_art ea
ON ea.event_id = e.id
WHERE e.cust_id = $cust_id
AND ea.art_id NOT IN
(
SELECT art_id
FROM price
WHERE cust_id = $cust_id
)
) e
JOIN price p
ON p.cust_id = 0
AND p.art_id = e.art_id
Make sure that (cust_id, art_id) (in this order) is a PRIMARY KEY or a UNIQUE INDEX on price.

Had to make some small changes to indicate which table was used where in the SQL, but pretty much copy&paste so not bad at all :P
SELECT price.art_id, price.price
FROM price
WHERE cust_id =114
UNION ALL
SELECT e.art_id, p.price
FROM (
SELECT DISTINCT art_id
FROM event e
JOIN event_art ea ON ea.event_id = e.id
WHERE e.cust_id =114
AND ea.art_id NOT
IN (
SELECT price.art_id
FROM price
WHERE cust_id =114
)
)e
JOIN price p ON p.cust_id =0
AND p.art_id = e.art_id

SELECT DISTINCT
a.id,
a.art_name,
COALESCE(p.price, p0.price) AS price
FROM event e
INNER JOIN event_art ea ON e.id = ea.event_id
INNER JOIN art a ON ea
LEFT JOIN price p ON p.art_id = a.id AND p.cust_id = e.cust_id
LEFT JOIN price p ON p.art_id = a.id AND p.cust_id = 0

Related

How to make a SQL query

I'm new to SQL, so I've got some troubles with creating queries.
My task is: To select a description of a product which was selling the most in 1989 with maximum discount.Product Table. Price table. What I tried to do is
Select maximum discount by subtracting list_price - min_price
select max(list_price - min_price) from PRICE
Select description
select description from product
join price on PRODUCT.product_id = PRICE.product_id
where start_date = '1989'
The problem is that I can't make it in one query
select top 1
product.product_id,
description,
max_disc = max(list_price - min_price)
from PRICE
join product on product.product_id = PRICE.product_id
where start_date = '1989'
group by product.product_id, description
order by max_disc desc
Inner Join between the two tables using product_id, and calculate ist_price - min_price AS discount
Filter out the resultset to carry prices of 1989 only, using where YEAR(start_date) = 1989
Consider the resultset as a Derived Table, and sort it using discount value in descending order. Use Limit 1 to find the product with maximum discount given in the year 1989.
For MySQL, try the following:
SELECT dt.*
FROM (
SELECT
pt.product_id,
pt.description,
pe.list_price - pe.min_price AS discount
FROM PRODUCT AS pt
JOIN PRICE AS pe ON pt.product_id = pe.product_id
WHERE YEAR(pe.start_date) = 1989
) AS dt
ORDER BY dt.discount DESC LIMIT 1
you try like below
select * from
(
select description from product
join price
on PRODUCT.product_id = PRICE.product_id
where start_date = '1989'
) t1
cross join (select max(list_price - min_price) as p from PRICE) t

MySQL - Marking duplicates from several table fields, as well as data from another table

I have two tables - one shows user purchases, and one shows a product id with it's corresponding product type.
My client wants to make duplicate users inactive based on last name and email address, but wants to run the query by product type (based on what type of product they purchased), and only wants to include user_ids who haven't purchased paint (product ids 5 and 6). So the query will be run multiple times - once for all people who have purchased lawnmowers, and then for all people who have purchased leafblowers etc (and there will be some overlap between these two). No user_id that has purchased paint should be made inactive.
In terms of who should stay active among the duplicates, the one to stay active will be the one with the highest product id purchased (as products are released annually). If they have multiple records with the same product id, the record to stay active will be the one with most recent d_modified and t_modified.
I also want to shift the current value of 'inactive' to the 'previously_inactive' column, so that this can be easily reversed if need be.
Here is some sample table data
If the query was run by leafblower purchases, rows 5, 6, and 7 would be made inactive. This is the expected output:
If the query was run by lawnmower purchases, rows 1 and 2 would be made inactive. This would be the expected output:
If row 4 was not the most recent, it would still not be made inactive, as user_id 888 had bought paint (and we want to exclude these user_ids from being made inactive).
This is an un-optimised version of the query for 'leafblower' purchases (it is working, but will probably be too slow in the interface):
UPDATE test.user_purchases
SET inactive = 1
WHERE id IN (
SELECT z.id
FROM (SELECT * FROM test.user_purchases) z
WHERE z.product_id IN (
SELECT product_id
FROM test.products
WHERE product_type IN ("leafblower")
)
AND id NOT IN (
SELECT a.id
FROM (SELECT * FROM test.user_purchases) a
INNER JOIN (
SELECT r.surname, r.email
FROM (SELECT * FROM test.user_purchases) r
JOIN test.products s on r.product_id = s.product_id
WHERE s.product_type IN ("paint")
) b
WHERE a.surname = b.surname
AND a.email = b.email
)
AND id NOT IN (
SELECT MAX(z.id)
FROM (SELECT * FROM test.user_purchases) z
WHERE z.product_id IN (
SELECT product_id
FROM test.products
WHERE product_type IN ("leafblower")
)
AND id NOT IN (
SELECT a.id
FROM (SELECT * FROM test.user_purchases) a
INNER JOIN (
SELECT r.surname, r.email
FROM (SELECT * FROM test.user_purchases) r
JOIN test.products s on r.product_id = s.product_id
WHERE s.product_type IN ("paint")
) b
WHERE a.surname = b.surname
AND a.email = b.email
)
GROUP BY surname, email
)
)
Any suggestions on how I can streamline this query and optimise the speed of it would be much appreciated.

Get cheapest price for a product

I have two tables: products and prices
products
id (PK)
name
prices
id (PK)
product_id (FK > products)
price
originalPrice
Each product might have multiple prices. What I want to achieve is a query that returns me all products on-sale with its cheapest price.
on-sale = price < originalPrice
if a product is not on-sale, it should not be included in the results
if a product has multiple prices that qualify for on-sale, only return the cheapest price.
The resulting table should have these columns
products.id
products.name
prices.id
prices.price
prices.originalPrice
With my attempts I'm ending up with this issue: #1055 - Expression #3 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'tbl.price' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by. Please note that I cannot change the config.
MySQL version: 5.7.22
I have uploaded a SQL export with sample data here: https://www.dropbox.com/s/6ucdv6592dum6n6/stackoverflow_export.sql?dl=0
select pro.name, MIN(pri.price) from products pro
inner join price pri on pri.product_id = pro.id
where pri.price < pri.originalPrice
group by pro.name
heres a shot without any data :p may need a little tweaking
Try this:
SELECT *
FROM `products` pro
JOIN price pri on pri.productId = pro.id
WHERE pri.price < pri.originalPrice
AND pri.price =
(
SELECT min(p.price)
FROM price p
WHERE p.productId = pro.id AND p.price < p.originalPrice
)
Hope this works for you
SELECT *,MIN(price) FROM (
SELECT name, products.id,price
FROM products
INNER JOIN productItems
ON products.id = productItems.productId
WHERE price < originalPrice
ORDER BY (price-originalPrice)
) as tbl GROUP BY id;
OR
SELECT *,MIN(diff) FROM (
SELECT name, products.id,price,(price-originalPrice) as "diff"
FROM products
INNER JOIN productItems
ON products.id = productItems.productId
WHERE price < originalPrice
ORDER BY products.id,(price-originalPrice)
) as tbl GROUP BY id;
This works with that dropbox link you gave: http://sqlfiddle.com/#!9/a6306d/3
select pro.name, MIN(pri.price) from products pro
inner join price pri on pri.productId = pro.id
where pri.price < pri.originalPrice
group by pro.name

Find properties by availability in foreign table using MySQL

Below are my tables
properties bookings
========== ==========
property_id booking_id
price property_id
city checkin_date
checkout_date
I have two tables, Properties and Bookings for a rental site. I want to do a search by check in date, check out date, price and location. It will check the availability from the foreign table bookings via the property_id FK.
Basically I want it to be something like:
SELECT property_id,price,city FROM properties
WHERE
price > 200,
city = "Toronto",
LEFT JOIN (
SELECT postid, COUNT( * ) AS total
FROM bookings
WHERE checkin_date *** SOMEHTING HERE ****
I am sure the above is incorrect already. Whether if I am to use left join or inner join.
SELECT DISTINCT p.property_id, p.price, p.city
FROM properties AS p
LEFT JOIN bookins AS b ON p.property_id = b.property_id
WHERE p.price > 200
AND p.city = 'Toronto'
AND (b.checkin_date >= '?' OR b.checkout_date <= '?')
replace ? with your search date

WHERE value IS NOT IN (subquery)

I've been struggling with this query.
I have two tables. One with coupons and Invoicenumbers. One with Invoicenumbers and customer names.
I need to get the customers who have not used a given coupon.
Here are the tables:
Promotion table:
Promotions
Invoice | Coupon
----------------
1 | couponA
2 | couponB
3 | couponB
Orders Table:
Orders
Invoice | Customer
------------------
1 | Jack
2 | Jack
3 | Jill
So Jack has used coupons A and B. And Jill has only used coupon B.
If my query were select customers who have not used coupon A, I should get Jill.
This works, but it seems clumsy and slow. Is there a better way?
SELECT Customer
FROM Promotions INNER JOIN Orders
ON Promotions.Invoice = Orders.Invoice
WHERE Customer NOT IN(
SELECT Customer
FROM Promotions INNER JOIN Orders
ON Promotions.Invoice = Orders.Invoice
WHERE Coupon = couponA)
GROUP BY Customer
Thanks for looking!
edit:
Here's an SQLFiddle schema
http://sqlfiddle.com/#!2/21d31/6
Updated: We should use prefer to use joins for better performance when its easy to do for us. Join vs. sub-query
Sql Fiddle
Select distinct Customer from orders o
join
(
SELECT distinct Customer as changedname FROM Orders o2
join
(
Select distinct invoice from Promotions where Coupon='couponA'
) t3
on o2.invoice = t3.invoice
) t2
on o.customer != t2.changedname;
Note: I changed column name customer for t3 because two joined tables must have different column names
Explanation:
Using inner or sub query is expensive when you have big data. use joins instead, lets learn converting subquery to join
With Subquery We had:
Select distinct Customer from orders where customer not in
(SELECT distinct Customer FROM Orders where invoice in
(Select distinct invoice from Promotions where Coupon='couponA'));
Converting sub-query to join
First step:
Select distinct Customer from orders o
join
(
SELECT distinct Customer as changedname FROM Orders where invoice in
(Select distinct invoice from Promotions where Coupon='couponA')
) t2
on o.customer != t2.changedname;
2nd step:
Select distinct Customer from orders o
join
(
SELECT distinct Customer as changedname FROM Orders o2 where invoice
join
(
Select distinct invoice from Promotions where Coupon='couponA'
) t3
on o2.invoice = t3.invoice
) t2
on o.customer != t2.changedname;
And that's it, much faster for tables having numerous rows
Original answer:
Use not in. Have a look.
Select distinct Customer from orders where customer not in
(SELECT distinct Customer FROM Orders where invoice in
(Select distinct invoice from Promotions where Coupon='couponA'));
Edit I have added distinct to make query faster
SQL Fiddle
SELECT DISTINCT o2.customer FROM ORDER o2
LEFT JOIN (promotions p1
JOIN Orders o1 ON p1.cuopon = 'CuoponA' AND p1.invoice = o1.invoice ) p3
ON o2.customer = p3.customer
WHERE p3.customer IS NULL
Try this query instead:
SELECT DISTINCT Customer
FROM Orders o1
WHERE NOT EXISTS (
SELECT 1
FROM Orders o2
INNER JOIN Promotions ON Promotions.Invoice = o2.Invoice
WHERE o1.Customer = o2.Customer AND Coupon = 'couponB')
The idea is to get rid of the GROUP BY by removing a join in the top part of the query, and also eliminate the NOT IN by making a coordinated subquery.
Here is a link to sqlfiddle.
Try this with a right join
SELECT Customer, Coupon
FROM Promotions
RIGHT JOIN Orders ON Promotions.Invoice = Orders.Invoice
AND Coupon = 'couponA'
GROUP BY Customer
HAVING Coupon IS NULL