How do optimise sql query using join between multiple tables - mysql

i have two tables having following structure
Table A
itemId categoryId orderDate
==========================================
1 23 2016-11-08
1 23 2016-11-12
1 23 2016-11-16
Table B have the structure
categoryId stock price
==========================================
23 500 600
However mine desired output should be as like
Result C
price stock orderdate qty
600 500 2016-11-08 (first order date) 3 (3 time appearance in first table)
Here is what i have tried so far
select b.price,b.stock from B b, A a
where b.categoryId = (
select a.categoryId
from A
GROUP BY categoryId
HAVING COUNT(categoryId)>1
)
and (a.orderdate = (
select MIN(orderdate)
from A
where categoryId = b.categoryId)
)
i have following result
price stock orderdate
600 500 2016-11-08
i have no idea how do find qty as it is appeared 3 times in first table.

I think you want the records in table a grouped by item id and category id, so include these two in your group by statement. Then the other columns you have to aggregate using MIN, MAX, AVG, SUM, etc. I use MIN which will give you the smallest number in the group for that particular column, although it shouldn't matter in this case whether you use MIN or MAX or AVG - it's all the same. Then COUNT(*) will just count the number of recrods in the group.
Also, joins are generally preferred over listing tables with commas.
SELECT a.itemid, a.categoryid, MIN(b.price), MIN(b.stock), min(a.orderdate), count(*) as qty
FROM a
INNER JOIN b ON a.categoryid = b.categoryid
GROUP BY a.itemid, a.categoryid

You also need to select COUNT(*)

how about use following sql
select min(price), min(stock), min(orderDate), COUNT(categoryId)
from A,B where A.categoryId = B.categoryId
GROUP by A.categoryId

You could create views for your subqueries and give them meaningful names e.g. CategoriesUsedInMultipleOrders, MostRecentOrderByCategory. This would 'optimize' you query by abstracting away complexity and making it easier for the human reader to understand.

This is the Query with the appropriate join method see Result:
SELECT B.price, B.stock, MIN( A.orderDate ) AS orderdate, COUNT( * ) AS qty
FROM TableA A, TableB B
WHERE A.categoryId = B.CategoryId
GROUP BY A.categoryId, B.price, B.stock

Related

Difficult order / group by / count

I've got 3 simple tables, but my query is difficult
Sellers table :
seller_id | name
1 john
2 paul
5 fred
6 robert
etc ...
Transactions table (only 3 values for the moment) :
trans_id | name
1 BUY
2 SELL
3 EXCHANGE
Operations table :
seller_id | trans_id | datetime
2 1 ....
2 2 ....
6 1 ....
2 3 ....
6 1 ....
This tables records all the sellers' transactions and their moment.
I would like to obtain, in the last day, or in a time interval
seller name, number of buy-transaction order by number of buy-transaction in the interval
seller name, number of buy-or-sell-transaction order by number buy-or-sell-transaction in the interval
I've tried many things, strange things taht mysql does'nt like, but I can't succeed ... thanks !
Here is a solution for your first query, ie "seller name, number of buy-transaction order by number of buy-transaction in the interval" :
SELECT
S.name,
COUNT(1) AS total
FROM operations O
JOIN sellers S on O.seller_id = S.seller_id
JOIN transactions T on O.trans_id = T.trans_id
WHERE O.datetime >= CURDATE() AND T.name = 'BUY'
GROUP BY S.name
ORDER BY total
Obviously, the second query is nearly the same, the where clause just changes a little, see the following :
SELECT
S.name,
COUNT(1) AS total
FROM operations O
JOIN sellers S on O.seller_id = S.seller_id
JOIN transactions T on O.trans_id = T.trans_id
WHERE O.datetime >= CURDATE()-1 AND T.name IN ('BUY','SELL')
GROUP BY S.name
ORDER BY total
To be honest you could even remove the join with transactions table and use O.trans_id in your where clause.
SEE DEMO HERE
Here solution for your first query
Select a.name, a.count from
(select seller.name name, Count(Transaction.Tran_id) count
from seller, Transaction, Operation
where seller.Seller_id = Operation.Seller_Id
and Transaction.Tran_id = Operation.Tran_id
and Transaction.Tran_id=1
and Operation.datetime between (datetime 1 & datetime2)
group by seller.name) a
order by a.count
Second solution is :-
Select a.name, a.count from
(select seller.name name, Count(Transaction.Tran_id) count
from seller, Transaction, Operation
where seller.Seller_id = Operation.Seller_Id
and Transaction.Tran_id = Operation.Tran_id
and Transaction.Tran_id in (1, 2)
and Operation.datetime between (datetime 1 & datetime2)
group by seller.name) a
order by a.count

Select maximum of groups

we have a table as the following which contains an order status:
history
---------------------------------
order_id status date
25 1 2014-10-01
25 3 2014-10-02
26 1 2014-10-01
27 2 2014-10-01
26 4 2014-10-03
I need to select the order_id of columns with a specified status, but it has to be the latest status.
This is possible with the group by function:
SELECT status, order_id, MAX(date)
FROM history GROUP BY order_id HAVING status = 4 OR status = 2
But this is not suitable for me, because I ONLY want the order_id, because this is a part of a subquery (SELECT ... WHERE order_id IN ([above query])). However, MySQL doesn't allow this.
Is there any alternative way to solve this?
This problem is a variation of the standard groupwise max problem, widely discussed here and elsewhere - and even with its own dedicated chapter in the mysql manual.
The standard response to the standard problem is the employment of an uncorrelated subquery, as follows...
SELECT x.*
FROM history x
JOIN
( SELECT order_id, MAX(date) max_date FROM history GROUP BY order_id ) y
ON y.order_id = x.order_id
AND y.max_date = x.date;
Your problem appears to be more complicated because of the additional requirement. However, this is a red herring. The correct answer using this method then is as follows...
SELECT x.*
FROM history x
JOIN
( SELECT order_id, MAX(date) max_date FROM history GROUP BY order_id ) y
ON y.order_id = x.order_id
AND y.max_date = x.date
WHERE status IN (2,4);
use SELF JOIN
SELECT h1.`order_id` FROM `history` h1
LEFT JOIN `history` h2
ON (h1.`order_id`= h2.`order_id` AND h1.`status` > h2.`status`)
-- WHERE h1.status IN (1,2,3) < -- here you can use your status condition
GROUP BY h1.`order_id`
Reference

How to get rows with max date when grouping in MySQL?

I have a table with prices and dates on product:
id
product
price
date
I create a new record when price change. And I have a table like this:
id product price date
1 1 10 2014-01-01
2 1 20 2014-02-17
3 1 5 2014-03-28
4 2 25 2014-01-05
5 2 12 2014-02-08
6 2 30 2014-03-12
I want to get last price for all products. But when I group with "product", I can't get a price from a row with maximum date.
I can use MAX(), MIN() or COUNT() function in request, but I need a result based on other value.
I want something like this in final:
product price date
1 5 2014-03-28
2 30 2014-03-12
But I don't know how. May be like this:
SELECT product, {price with max date}, {max date}
FROM table
GROUP BY product
Alternatively, you can have subquery to get the latest get for every product and join the result on the table itself to get the other columns.
SELECT a.*
FROM tableName a
INNER JOIN
(
SELECT product, MAX(date) mxdate
FROM tableName
GROUP BY product
) b ON a.product = b.product
AND a.date = b.mxdate
I think the easiest way is a substring_index()/group_concat() trick:
SELECT product,
substring_index(group_concat(price order by date desc), ',', 1) as PriceOnMaxDate
max(date)
FROM table
GROUP BY product;
Another way, that might be more efficient than a group by is:
select p.*
from table t
where not exists (select 1
from table t2
where t2.product = t.product and
t2.date > t.date
);
This says: "Get me all rows from the table where the same product does not have a larger date." That is a fancy way of saying "get me the row with the maximum date for each product."
Note that there is a subtle difference: the second form will return all rows that on the maximum date, if there are duplicates.
Also, for performance an index on table(product, date) is recommended.
You can use a subquery that groups by product and return the maximum date for every product, and join this subquery back to the products table:
SELECT
p.product,
p.price,
p.date
FROM
products p INNER JOIN (
SELECT
product,
MAX(date) AS max_date
FROM
products
GROUP BY
product) m
ON p.product = m.product AND p.date = m.max_date
SELECT
product,
price,
date
FROM
(SELECT
product,
price,
date
FROM table_name ORDER BY date DESC) AS t1
GROUP BY product;

find price difference for different date

how find difference of price for two selected day. my table as shown
---------------------------------------
id price date product
---------------------------------------
1 10 15-12-2013 pen
2 40 15-12-2013 book
3 15 16-12-2013 pen
4 42 16-12-2013 book
-------------------------------------
i want an sql query to get output like, if startdate:15-12-2013 & enddate: 16-12-2013
product startdate(15-12-2013) enddate(16-12-2013) difference
--------------------------------------------------------------
pen 10 15 5
book 40 42 2
--------------------------------------------------------------
Maybe something like this?
select
p1.product,
p1.price,
p2.price,
p1.price - p2.price as diff
from
product p1,
product p2
where
p1.product=p2.product and
date(p1.date)='2013-12-15' and
date(p2.date)='2013-12-16'
If performance is a question then this link can provide a better alternative for the date matching part: MySQL SELECT WHERE datetime matches day (and not necessarily time)
Try this:
SELECT product, StartDatePrice, EndDatePrice, (EndDatePrice - StartDatePrice) AS Difference
FROM (SELECT product, MAX(IF(a.date = '15-12-2013', a.price, 0)) AS StartDatePrice,
MAX(IF(a.date = '16-12-2013', a.price, 0)) AS EndDatePrice
FROM tableA a
GROUP BY product
) AS A;
If you insert a row on your prices table whenewer a price changes, and not every day, you should consider using this query:
SELECT
p1.product,
p1.price as stardtade,
p2.price as enddate,
p2.price-p1.price as difference
FROM
prices p1 INNER JOIN (SELECT product, MAX(dt) max_dt
FROM prices
WHERE dt<='2013-12-15'
GROUP BY product) st
ON p1.product=st.product AND p1.dt = st.max_dt
INNER JOIN
prices p2
ON p1.product=p2.product
INNER JOIN (SELECT product, MAX(dt) max_dt
FROM prices
WHERE dt<='2013-12-16'
GROUP BY product) ed
ON p2.product=ed.product AND p2.dt = ed.max_dt
it is more complicated, but it will work even if some dates are not present in your table. In that case it will use the lask known value for the price.
Please see fiddle here.

MYSQL Query for SUM and DISTINCT?

I have a database with the first five columns like this:
ID NAME QUANTITY PRICE KIND
1 Dog 2 5 A
2 Cat 1 6 B
3 Dog 2 5 C
4 Bird 5 5 C
(DOG QUANTITY and PRICE will always be the same)
What I want to do to is to something like
SELECT KIND, SUM(QUANTITY * PRICE) GROUP BY KIND WHERE DISTINCT NAME
So that I get something that looks like this:
A 10
B 6
C 25
(The duplicate DOG is eliminated.)
I know my syntax above is grossly wrong -- it's just seems to be the most eloquent way of explaining what sort of thing I'm looking for.
In other words, I want to get rid of non-distinct NAMES then SUM the rest. I seem to be able to do one or the other but not both.
Any ideas? If worse comes to worst I can do it as a loop in PHP rather than as a single MYSQL query.
I'm not really clear about either what the rules are or why your table is in that format (with repeated name, quantity,price) but here is one way of getting your expected output.
select kind, SUM(quantity*price)
from
(
SELECT name, quantity, price, min(kind) kind
FROM YourTable
group by name, quantity, price
) t
group by kind
Here I chose the item with the lowest ID as the one to keep:
Select T.Kind, Sum( T.Quantity * T.Price ) As Total
From Table As T
Where Id = (
Select Min(T2.Id)
From Table As T2
Where T2.Name = T.Name
)
Group By T.Kind
Assuming that your table is unique on Name and Kind, you can do:
Select T.Kind, Sum( T.Quantity * T.Price ) As Total
From Table As T
Where T.Kind = (
Select Min(T2.Kind)
From Table As T2
Where T2.Name = T.Name
)
Group By T.Kind