same SQL subquery with WHERE and FROM - mysql

Using the table below
It can also be easily replaced with an OUTER JOIN whenever a need arises.
The WHERE syntax is more relational model oriented.
A result of two tables JOIN'ed is a cartesian product of the tables to which a filter is applied which selects only those rows with joining columns matching.
It's easier to see this with the WHERE syntax.
SELECT Toy_name, Quantity
FROM Toy T,Hire Transaction H, Store S
WHERE S.Store_ID = T.store_id
AND t.toy_id = H.toy_id
AND t.hire_price = (SELECT max(hire_price) from Toy)
AND UPPER(S.store_suburb = ‘SCARSDALE’)
AND H.hire_date >= ’01/02/2013’
AND H.hire_date <= ’31/03/2015’;
I was able to write this. But how can I solve this using a subquery as asked?

You could avoid the sub select, by ordering your result by descending price, and then limiting the output to just one record:
SELECT Toy_name,
Sum(H.Quantity) total_quantity
FROM Toy T
INNER JOIN `Hire Transaction` H
ON H.Toy_id = t.Toy_id
INNER JOIN Store S
ON S.Store_ID = T.store_id
WHERE upper(S.store_suburb) = 'SCARSDALE'
AND H.hire_date BETWEEN '2013-02-01' AND '2015-03-31'
GROUP BY T.Toy_id
ORDER BY T.Hire_price DESC
LIMIT 1

You can try this
SELECT t.toy_name, sum(ht.quantity) quantity, max(t.hire_price) max_p
FROM toy t
INNER JOIN hire_transaction ht on t.toy_id = ht.toy_id
INNER JOIN store s on t.store_id = s.store_id
WHERE ht.hire_date between '2013-02-01' and '2015-03-31' and upper(s.store_suburb) = 'SCARSDALE'
group by t.toy_id
having max(max_p)

select h.toy_id, sum(h.Quantity) as hire_count from HireTransaction as h where h.hire_date >= ’01/02/2013’
AND h.hire_date <= ’31/03/2015’ group by h.toy_id having h.toy_id = (select toy_id from Toy where hire_price = (select max(hire_price) from Toy) and store_id = (select store_id from Store where store_suburb = 'SCARSDALE')

Related

Return single record from another table with multiple records per transactionDate

How can I create a query that will return the latest Soa LoanableAmount based on the LoanApplicationDate.
Ex:
For LoanID = 1, I want to get the Soa record with SoaID = 2 since this is the latest loanable amount for this LoanApplicationDate - 2017-07-01.
So far this is what I have accomplished:
select *
from Loan L
join Soa S
ON S.EmployeeID = L.EmployeeID
where S.TransactionDate <= L.LoanApplicationDate
To illustrate what I want to accomplish, please see screenshot below.
https://www.db-fiddle.com/f/3PBossUJLYQTQZJfZymiph/0
I think you want a join and then to filter to get the most recent date:
select l.*, s.*
from Loan l join
Soa s
on s.employeeid = l.employeeid
where s.TransactionDate <= (select max(s2.TransactionDate)
from Soa s2
where s2.employeeid = l.employeeid and
s2.TransactionDate <= l.LoanApplicationDate
);
I think you need to set the condition on the join:
select
L.LoanID,
L.EmployeeID,
L.LoanAmount,
L.LoanApplicationDate,
S.LoanableAmount,
S.TransactionDate
from Loan L join Soa S
ON S.EmployeeID = L.EmployeeID and
S.TransactionDate = (select max(TransactionDate) from Soa where TransactionDate <= L.LoanApplicationDate)
See the demo
SQL DEMO
select *
from Loan L
join Soa S
ON S.EmployeeID = L.EmployeeID
and S.TransactionDate =
(select max(S2.TransactionDate)
from Soa S2
where S2.TransactionDate <= L.LoanApplicationDate
and S2.`EmployeeID` = L.`EmployeeID`
)
The below query will give you all employee ids with loanable amount withdrawn on latest date
Select employee_id,
LoanableAmount
transactionDate from table
having
transactionDate=max(transactionDate)
group by employee_id
You need to run a subquery for each row:
select q.*, S.LoanableAmount, S.TransactionDate from
(select
L.*
,(select SoaID from Soa x where x.EmployeeID = L.EmployeeID and x.TransactionDate <= L.LoanApplicationDate order by TransactionDate DESC limit 1) as SoaID
from Loan L) q
join Soa S on S.SoaID = q.SoaID
Line 4 will give you the SoaID of the row in the Soa table that has the newest date that is less than or equal to the Application Date, then with that ID you just join and show the rest of the fields.

Sql trouble with coalesce() not working propely

i have a query and i'm having trouble to change the name of the last row of columb name to 'TOTAL'. The result gives me the same name of the row above the last row.
Here's my query:
SELECT COALESCE(ticket_types.name,'TOTAL') AS name,
COUNT(1) AS quantity
FROM tr_logs
LEFT JOIN tickets ON tr_logs.value = tickets.id
LEFT JOIN ticket_types ON tickets.ticket_type_id = ticket_types.id
LEFT JOIN transactions ON tr_logs.transaction_id = transactions.id
LEFT JOIN tr_fields_data AS tfd_shipping ON tfd_shipping.transaction_id = transactions.id
WHERE type = 'ADDITEM'
AND transactions.event_id = '46'
AND DATE(tr_logs.created_date)
BETWEEN '2017-03-26' AND '2017-05-24'
AND tfd_shipping.data IN ('0','570','571','771')
AND name IS NOT NULL
GROUP BY ticket_types.id WITH ROLLUP
The result looks like this:
name quantity
premium 56
outlaw 6
outlaw 62
Last row name from rollup is not null.... I need it to be TOTAL and not outlaw
Thanks
You haven't changed the name to TOTAL at all: you've changed the name of the column to name, and you've told it to replace any null values with TOTAL.
If you want to change the name of ticket_types.name to total, you just want
SELECT ticket_types.name AS total ...
(But it would be weird to rename something called name to total, so perhaps you need to clarify your requirements a little.)
This may or not be related to your observed problem, but the WHERE and GROUP BY clauses turn all the outer joins into inner joins. You should simplify the query to:
SELECT COALESCE(tt.name, 'TOTAL') AS name, COUNT(1) AS quantity
FROM tr_logs l JOIN
tickets
ON l.value = t.id JOIN
ticket_types tt
ON t.ticket_type_id = tt.id JOIN
transactions tr
ON l.transaction_id = tr.id JOIN
tr_fields_data fd
ON fd.transaction_id = tr.id
WHERE type = 'ADDITEM' AND
tr.event_id = '46' AND
DATE(l.created_date) BETWEEN '2017-03-26' AND '2017-05-24' AND
fd.data IN ('0', '570', '571', '771') AND
tt.name IS NOT NULL
GROUP BY tt.id WITH ROLLUP
Thanks to Gordon Linoff I have figure out my problem.
The name of the last row was never null beacause i GROUP BY with a different attribute.
Here's the solution.
SELECT COALESCE(tckn,'TOTAL') AS name, quantity FROM
(SELECT tt.name AS tckn, COUNT(1) AS quantity
FROM tr_logs AS l
LEFT JOIN tickets AS t ON l.value = t.id
LEFT JOIN ticket_types AS tt ON t.ticket_type_id = tt.id
LEFT JOIN transactions AS tr ON l.transaction_id = tr.id
LEFT JOIN tr_fields_data AS tfd ON tfd.transaction_id = tr.id
WHERE type = 'ADDITEM'
AND tr.event_id = '46'
AND DATE(l.created_date)
BETWEEN '2017-03-26' AND '2017-05-24'
AND tfd.data IN ('0','570','571','771')
GROUP BY tckn WITH ROLLUP) as sum;

GROUP BY from inner SELECT suquery is ignored in column sum

I have following query
SELECT YEAR(T.date), MONTH(T.date), T.production, T.lineID, SUM(rework + scrap)
FROM
(SELECT MAX(positionID), date, production, lineID
FROM productionPerPosition
WHERE lineID = 2
AND date BETWEEN '2017-01-01' AND '2017-01-31'
GROUP BY date) AS T
INNER JOIN linePosition lp ON lp.lineID = T.lineID
INNER JOIN fttErrorType fet ON fet.positionID = lp.positionID
INNER JOIN fttData fd ON fd.errorID = fet.errorID
AND fd.date = T.date
GROUP BY YEAR(T.date), MONTH(T.date)
which gives this result
Now, I would like to group these results by year and month to get sum of production and sum of last column. I've tried this query
SELECT YEAR(T.date), MONTH(T.date), SUM(T.production), T.lineID, SUM(rework + scrap)
FROM
(SELECT MAX(positionID), date, production, lineID
FROM productionPerPosition
WHERE lineID = 2
AND date BETWEEN '2017-01-01' AND '2017-01-31'
GROUP BY date) AS T
INNER JOIN linePosition lp ON lp.lineID = T.lineID
INNER JOIN fttErrorType fet ON fet.positionID = lp.positionID
INNER JOIN fttData fd ON fd.errorID = fet.errorID
AND fd.date = T.date
GROUP BY YEAR(T.date), MONTH(T.date)
Which gives me
Here production sum is wrong! It seems that GROUP BY from 7th line in first query is ignored.
Any idea how could I get needed result?
Edit: In inner SELECT I have separate production for several different positions (positionID) but I'm using only production from position that has highest positionID
Group has missing grouping columns that why it is resulting in some unexpected result
SELECT YEAR(T.date), MONTH(T.date), SUM(T.production), T.lineID, SUM(rework + scrap)
FROM
(SELECT MAX(positionID), date, production, lineID
FROM productionPerPosition
WHERE lineID = 2
AND date BETWEEN '2017-01-01' AND '2017-01-31'
GROUP BY date, production, lineID) AS T
INNER JOIN linePosition lp ON lp.lineID = T.lineID
INNER JOIN fttErrorType fet ON fet.positionID = lp.positionID
INNER JOIN fttData fd ON fd.errorID = fet.errorID
AND fd.date = T.date
GROUP BY YEAR(T.date), MONTH(T.date), T.lineID
Has explained in e4c5 comment, you have to add all the unaggregated fields to your GROUP BY. I made it in the inner SELECT and in the main SELECT:
SELECT YEAR(T.date), MONTH(T.date), SUM(T.production), T.lineID, SUM(rework + scrap)
FROM
(SELECT MAX(positionID), date, production, lineID
FROM productionPerPosition
WHERE lineID = 2
AND date BETWEEN '2017-01-01' AND '2017-01-31'
GROUP BY date, production, lineID) AS T
INNER JOIN linePosition lp ON lp.lineID = T.lineID
INNER JOIN fttErrorType fet ON fet.positionID = lp.positionID
INNER JOIN fttData fd ON fd.errorID = fet.errorID
AND fd.date = T.date
GROUP BY YEAR(T.date), MONTH(T.date), T.lineID

MySQL DISTINCT not Filtering out

I have the folowing sql query:
SELECT DISTINCT(tbl_products.product_id), tbl_products.product_title,
tbl_brands.brand_name, tbl_reviews.review_date_added,
NOW() AS time_now
FROM tbl_products, tbl_reviews, tbl_brands
WHERE tbl_products.product_id = tbl_reviews.product_id AND
tbl_products.brand_id = tbl_brands.brand_id
ORDER BY tbl_reviews.review_date_added DESC
That needs to filter out any duplicate product_id's unfortunatly selecting tbl_reviews.review_date_added makes each record unique which means DISTINCT will not work anymore.
Is there any otherway of doing this query so that product_id is still unique?
I did do the GROUP BY and the problem is I display the tbl_reviews.review_date_added on a website and it selects the oldest date. I need the newest date.
Regards
With the description given, it's a bit hard to be certain, but if review_date_added is the only problem, it seems like you want the MAX() of that date?
If the following doesn't help, please could you give example data, example output, and a description of how you want the output to be created?
SELECT
tbl_products.product_id,
tbl_products.product_title,
tbl_brands.brand_name,
MAX(tbl_reviews.review_date_added) AS review_date_added,
NOW() AS time_now
FROM
tbl_products
INNER JOIN
tbl_reviews
ON tbl_products.product_id = tbl_reviews.product_id
INNER JOIN
tbl_brands
ON tbl_products.brand_id = tbl_brands.brand_id
GROUP BY
tbl_products.product_id,
tbl_products.product_title,
tbl_brands.brand_name
ORDER BY
MAX(tbl_reviews.review_date_added) DESC
Distinct works for the entire row. The parenthesis are just around the field:
distinct (a), b, c === distinct a, b, c
A straightforward solution is group by. You can use min to select the oldest date.
select tbl_products.product_id
, min(tbl_products.product_title)
, min(tbl_brands.brand_name)
, min(tbl_reviews.review_date_added)
, NOW() AS time_now
FROM tbl_products, tbl_reviews, tbl_brands
WHERE tbl_products.product_id = tbl_reviews.product_id AND
tbl_products.brand_id = tbl_brands.brand_id
GROUP BY
tbl_products.product_id
ORDER BY
min(tbl_reviews.review_date_added) DESC
Note that if a product can have multiple brands, this will pick the lowest one.
Try this:
SELECT pr.product_id, pr.product_title,
bd.brand_name,
(SELECT MAX(rev.review_date_added) FROM tbl_reviews rev
WHERE pr.product_id = rev.product_id) AS maxdate,
NOW() AS time_now
FROM tbl_products pr INNER JOIN tbl_reviews re
ON pr.product_id = re.product_id
INNER JOIN tbl_brands bd
ON pr.brand_id = bd.brand_id
GROUP BY pr.product_id
ORDER BY re.review_date_added DESC
or (as suggested by #Hogan)
SELECT pr.product_id, pr.product_title,
bd.brand_name, md.maxdate
NOW() AS time_now
FROM tbl_products pr INNER JOIN tbl_reviews re
ON pr.product_id = re.product_id
INNER JOIN tbl_brands bd
ON pr.brand_id = bd.brand_id
INNER JOIN (SELECT product_id, MAX(review_date_added) AS maxdate
FROM tbl_reviews rev GROUP BY product_id) md
ON pr.product_id = md.product_id
GROUP BY pr.product_id
ORDER BY re.review_date_added DESC
I combined the answer of Andomar with some changes you will find here.
SELECT tbl_products.product_id, tbl_products.product_title,
tbl_products.product_date_added, tbl_brands.brand_name,
MAX(tbl_reviews.review_date_added) AS review_date_added, NOW() AS time_now
FROM tbl_products, tbl_reviews, tbl_brands
WHERE tbl_products.product_id = tbl_reviews.product_id AND
tbl_products.brand_id = tbl_brands.brand_id
GROUP BY tbl_products.product_id
ORDER BY MAX(tbl_reviews.review_date_added) DESC
Works beautifully and shows the newest date at tbl_reviews.review_date_added.
Regards

MySQL LIMIT in a Correllated Subquery

I have a correlated subquery that will return a list of quantities, but I need the highest quantity, and only the highest. So I tried to introduce an order by and a LIMIT of 1 to achieve this, but MySQL throws an error stating it doesn't yet support limits in subqueries. Any thoughts on how to work around this?
SELECT Product.Name, ProductOption.Name, a.Qty, a.Price, SheetSize.UpgradeCost,
FinishType.Name, FinishOption.Name, FinishTierPrice.Qty, FinishTierPrice.Price
FROM `Product`
JOIN `ProductOption`
ON Product.idProduct = ProductOption.Product_idProduct
JOIN `ProductOptionTier` AS a
ON a.ProductOption_idProductOption = ProductOption.idProductOption
JOIN `PaperSize`
ON PaperSize.idPaperSize = ProductOption.PaperSize_idPaperSize
JOIN `SheetSize`
ON SheetSize.PaperSize_idPaperSize = PaperSize.idPaperSize
JOIN `FinishOption`
ON FinishOption.Product_idProduct = Product.idProduct
JOIN `FinishType`
ON FinishType.idFinishType = FinishOption.Finishtype_idFinishType
JOIN `FinishTierPrice`
ON FinishTierPrice.FinishOption_idFinishOption = FinishOption.idFinishOption
WHERE Product.idProduct = 1
AND FinishTierPrice.idFinishTierPrice IN (SELECT FinishTierPrice.idFinishTierPrice
FROM `FinishTierPrice`
WHERE FinishTierPrice.Qty <= a.Qty
ORDER BY a.Qty DESC
LIMIT 1)
This is a variation of the greatest-n-per-group problem that comes up frequently.
You want the single row form FinishTierPrice (call it p1), matching the FinishOption and with the greatest Qty, but still less than or equal to the Qty of the ProductOptionTier.
One way to do this is to try to match a second row (p2) from FinishTierPrice that would have the same FinishOption and a greater Qty. If no such row exists (use an outer join and test that it's NULL), then the row found by p1 is the greatest.
SELECT Product.Name, ProductOption.Name, a.Qty, a.Price, SheetSize.UpgradeCost,
FinishType.Name, FinishOption.Name, FinishTierPrice.Qty, FinishTierPrice.Price
FROM `Product`
JOIN `ProductOption`
ON Product.idProduct = ProductOption.Product_idProduct
JOIN `ProductOptionTier` AS a
ON a.ProductOption_idProductOption = ProductOption.idProductOption
JOIN `PaperSize`
ON PaperSize.idPaperSize = ProductOption.PaperSize_idPaperSize
JOIN `SheetSize`
ON SheetSize.PaperSize_idPaperSize = PaperSize.idPaperSize
JOIN `FinishOption`
ON FinishOption.Product_idProduct = Product.idProduct
JOIN `FinishType`
ON FinishType.idFinishType = FinishOption.Finishtype_idFinishType
JOIN `FinishTierPrice` AS p1
ON p1.FinishOption_idFinishOption = FinishOption.idFinishOption
AND p1.Qty <= a.Qty
LEFT OUTER JOIN `FinishTierPrice` AS p2
ON p2.FinishOption_idFinishOption = FinishOption.idFinishOption
AND p2.Qty <= a.Qty AND (p2.Qty > p1.Qty OR p2.Qty = p1.Qty
AND p2.idFinishTierPrice > p1.idFinishTierPrice)
WHERE Product.idProduct = 1
AND p2.idFinishTierPrice IS NULL