simple sql select over 3 tables - mysql

I'm stuck with Mysql... I'm sure this question have been asked hundreds of times but without any keyword. I've 2 tables and a view:
tbl_user_measure
measure_cat userid amount
------------------------------
7 1 78
5 1 96
4 1 78
tbl_itemcat_measure
measure_cat item_cat
----------------------
7 1
5 1
4 1
vw_allitems
measure_cat min max item_cat itemid
---------------------------------------------------
7 76 81 1 1
5 97 100 1 1
4 79 81 1 1
7 76 81 1 11
5 95 97.5 1 11
4 76 79 1 11
4 33 12 2 5
What I'm looking for is the item(s) whose min and max values match in all measure_cat's of the specific item_cat the users values (max > tbl_user_measure.amount > min).
My approach was
SELECT distinct
v.itemid,
v.measure_cat_id,
v.min,
v.max
FROM
vw_allitems v, tbl_user_measure um, tbl_user u
where
um.tbl_measure_category_id = v.measure_cat_id and
(um.amount >= v.min and um.amount <= v.max) and
v.productid = 1
This resultes in:
itemid measure_cat_id min max
---------------------------------------------------
1 7 76 81
7 7 76 81
10 7 76 81
11 7 76 81
11 4 76 79
11 5 95 9
What I need is just the itemid '11' because its values fit in all categories.

Looks like you're almost there. Your query returns all matching rows; change it so that it returns the itemid and the count, grouped by itemid. All that's left is just to return those items where the count is equal to the number of rows that user has in the first table.
UPDATE: This should do what you need:
SELECT x.itemid, x.measure_cat, x.min, x.max
FROM
(SELECT vw.itemid i, u.userid u, count(*) mc
FROM vw_allitems vw
JOIN tbl_user_measure u ON vw.measure_cat=u.measure_cat
WHERE u.amount>=vw.min and u.amount<=vw.max
GROUP BY vw.itemid, u.userid) match_counts
JOIN
(SELECT userid u, count(*) uc
FROM tbl_user_measure
GROUP BY userid) user_counts
ON match_counts.u = user_counts.u
AND match_counts.mc=user_counts.uc
JOIN
vw_allitems x
ON match_counts.i = x.itemid
WHERE match_counts.u=1
Quick walkthrough. The first subquery takes every combination of user and item, and counts the number of matches. The second subquery counts how many entries that user has in tbl_user_measure. The join selects just those users and items where the match count equals the total count for that user (i.e. all the user's entries match), then finally we join it to the original data to return the original rows from vw_allitems and select just the user we are interested in.

Try this query:
SELECT v.itemid,
v.measure_cat_id,
v.min,
v.max
FROM vw_allitems as v,
INNER JOIN tbl_user_measure as um
ON um.measure_cat = v.measure_cat and um.amount >= v.min and um.amount <= v.max
INNER JOIN tbl_itemcat_measure as im
ON im.item_cat=um.item_cat

Related

MYSQL GROUP BY and SUM with clause

Hello there are two tables
Interval
id
is_full
1
1
2
0
3
0
entry_penalty
interval_id
entry_id
amount
2
14
55
3
14
7
3
14
1
1
15
4
1
15
8
2
15
11
So i am trying to display Sum of all entry_penalties per interval, twist is even if there is no relation between entry_penalty and interval table i should display full course interval sum per entry_id (related to is_full field).
For example total results should be in this case
interval_id
entry_id
amount
1
14
63
2
14
55
3
14
8
1
15
23
2
15
11
I have tried with sub query but it ignores to do calculation when there is no relation between entry_penalties and interval tables regarding is_full column.
My code so far.
SELECT
ep.interval_id,
IF (
i.is_full,
(
SELECT SUM(ep2.amount) * 1000 FROM entry_penalty as ep2
WHERE ep2.entry_id = ep.entry_id
),
SUM(ep.amount) * 1000
) as penalty_time,
ep.entry_id
FROM entry_penalty ep
INNER JOIN \`interval\` i ON i.id = ep.interval_id
WHERE ep.entry_id IN (:entryIds)
GROUP BY interval_id, entry_id`
I would propose to deal with the two cases (full, not full) separately, and then use union all to combine the two results:
SELECT i.id, ep.entry_id, SUM(ep.amount)
FROM `interval` i,
entry_penalty ep
WHERE i.is_full
GROUP BY i.id, ep.entry_id
UNION ALL
SELECT i.id, ep.entry_id, SUM(ep.amount)
FROM entry_penalty ep
INNER JOIN `interval` i
ON ep.interval_id = i.id
AND NOT i.is_full
GROUP BY i.id, ep.entry_id
ORDER BY 2, 1
See it run on dbfiddle.uk, where it outputs:
id
entry_id
SUM(ep.amount)
1
14
63
2
14
55
3
14
8
1
15
23
2
15
11

mysql join two table rows in one table

I have two tables e_sku and e_availability, In e_sku table i have 45 rows with the default availability as 1, below is an example
id is_available name
1 1 UN001N
2 1 UN002N
3 1 UN003N
4 1 UN004N
5 1 UN005N
6 1 UN006N
7 1 UN007N
8 1 UN008N
9 1 UN009N
10 1 UN010N
11 1 UN011N
12 1 UN012N
13 1 UN013N
14 1 UN014N
15 1 UN015N
16 1 UN016N
17 1 UN017N
18 1 UN018N
19 1 UN019N
20 1 UN020N
21 1 UN021N
22 1 UN022N
23 1 UN023N
24 1 UN024N
25 1 UN025N
26 1 UN026N
27 1 UN027N
28 1 UN028N
29 1 UN029N
30 1 UN030N
31 1 UN031N
32 1 UN032N
33 1 UN033N
34 1 UN034N
35 1 UN035N
36 1 UN036N
37 1 UN037N
38 1 UN038N
39 1 UN039N
40 1 UN040N
41 1 UN041N
42 1 UN042N
43 1 UN043N
44 1 UN044N
45 1 UN045N
Second table is e_availability in this table i am only storing the unavailability with dates below is the example
id e_sku_id is_available working_date
1 5 0 10/20/2016
2 8 0 10/20/2016
3 10 0 10/20/2016
4 1 0 10/20/2016
5 15 0 10/20/2016
6 11 0 10/19/2016
7 1 0 10/19/2016
Because the data is capturing every date for unavailability so i am only gathering the unavailable product's data in e_availability table w.r.t. dates, Now i am looking to show the data for each day as complete skus in such a way that every day report will show 45 skus and for available sku it will show 1 from e_sku table's column is_available and for unavailable sku it will show the column of is_availability from e_availability table
I am using mysql database, I tried many join queries but not getting the report.
can any one guide about which join i require
I got the result by using this query
SELECT * FROM (SELECT id,NAME,1 AS is_available FROM e_sku
WHERE company_id = 2
AND id NOT IN (SELECT id FROM (SELECT e_sku.id,e_sku.name, edge_availability.is_available FROM edge_availability
JOIN edge_working ON edge_working.`id` = edge_availability.`working_id`
JOIN e_sku ON e_sku.id = edge_availability.`sku_id`
WHERE edge_working.`working_date` = '2016-10-19' AND edge_availability.`store_id` = 84) X)
UNION
SELECT e_sku.id,e_sku.name, edge_availability.is_available FROM edge_availability
JOIN edge_working ON edge_working.`id` = edge_availability.`working_id`
JOIN e_sku ON e_sku.id = edge_availability.`sku_id`
WHERE edge_working.`working_date` = '2016-10-19' AND edge_availability.`store_id` = 84) Y
ORDER BY id
You can use a LEFT JOIN to detect when a date isn't found in the e_availability table. The join will return NULL for all the columns in that table, then you can default to the value from e_sku.
SELECT d.date, s.id, IFNULL(a.is_available, s.is_available) AS is_available
FROM all_dates AS d
CROSS JOIN e_sku AS s
LEFT JOIN e_availability ON d.working_date = a.date AND s.id = a.e_sku_id
You need to create an additional table all_dates that contains all the dates that you want to report on. See What is the most straightforward way to pad empty dates in sql results (on either mysql or perl end)? for how you can create such a table dynamically.

How can I get the id, min and max in the same My SQL query?

I have 2 tables: stops and bus_route_details.
stop_id stop_name
1 ā€˜Cā€™ CROSS ROAD JUNCTION
2 10TH ROAD
3 16TH ROAD
4 4TH ROAD (GOLIBAR)
5 600TENAMENTGATE
6 A.D.MODI INSTITUTE
7 AHANSARI CHOWK
8 A.H.SCHOOL
9 A.P.M.COMPLEX
10 A.T.I.
11 AAI TULJABHAVANI CHOWK/LOKHANDWALA COMPLEX
12 AAKASH GANGA SOCIETY (DHARAVI)
The table stops simply stores the id and name of each stop. The bus_route_details table stores the bus_number, the stop_id of the stop from the stops table and the order in which the stop appears on that route. The first stop has the order 1 whereas the last stop can be a number like 44 if the route has 44 stops in total.
bus_number stop_id stop_order
8 2139 30
8 351 31
8 1791 32
8 19 33
8 2 34
8 497 35
8 2024 36
8 20 37
8 404 38
8 1787 39
8 621 40
8 1937 41
8 1941 42
7 509 1
7 788 2
7 996 3
7 1340 4
7 1161 5
7 335 6
7 2296 7
7 891 8
As per the above, I would like to get a table that shows where bus number 7 and 8 start and end.
In simple words, get the distinct list of bus numbers, find the stop which minimum stop order and maximum stop order for each bus.
7, start, end [This is how I want the bus numbers and stop names]
My current query only gives me 2 columns: either the starting or the ending stop_name. How can I retrieve all 3 columns in the same query?
SELECT bus_number, stop_name from bus_route_details, stops `WHERE(bus_number, stop_order) IN (SELECT bus_number, MAX(stop_order)`
FROM bus_route_details
GROUP BY bus_number)
AND stops.stop_id = bus_route_details.stop_id
Any suggestions? I did the UNION and got all 4 results successfully in 2 columns but I would like 3 columns for this. Thank you
One way of doing this is to find the min and max in a derived table and join the stops table and finally use conditional aggregation to flatten the result, like this:
select
b.Bus_number,
max(case when b.stop_order = x.mio then b.stop_id end) min_stop_id,
max(case when b.stop_order = x.mio then s.stop_name end) min_stop_name,
max(case when b.stop_order = x.mao then b.stop_id end) max_stop_id,
max(case when b.stop_order = x.mao then s.stop_name end) max_stop_name
from bus_route_details b
join (
select Bus_number, min(stop_order) mio, max(stop_order) mao
from bus_route_details
group by Bus_number
) x on b.Bus_number = x.Bus_number and (b.stop_order = x.mio or b.stop_order = x.mao)
join stops s on b.stop_id = s.stop_id or b.stop_id = s.stop_id
group by b.Bus_number;
This would give you a result like:
Bus_number min_stop_id min_stop_name max_stop_id max_stop_name
----------- ----------- -------------------- ----------- --------------------
7 509 stop 1 891 stop 2
8 351 stop 3 1941 stop 4
Use double inner join on stops
select b.stop_name as name_start, c.stop_name as finish_name
from
( SELECT bus_number, min(stop_order) as start_id, max(stop_order) as finish_id
from bus_route_details
group by bus_number) as t
inner join bus_route_details as b on t.start_id = b.stop_id
inner join bus_route_details as c on t.finish_id = c.stop_id

counting using join information

I have two tables.
One table records orderstock which has FK_stock and FK_orderNo
I want to count the number of orders each item of stock has. The following code works correctly to do this:
(1)
SELECT orderstock.FK_orderNo, Count(orderstock.FK_stock) AS CountOfFK_stock
FROM stock INNER JOIN orderstock ON stock.StockID = orderstock.FK_stock
GROUP BY orderdemo.FK_orderNo
However, I wish to add to this such that only stock items which are non perishable (stock.perishable=0) are listed. So something like
SELECT orderstock.FK_orderNo, Count(orderstock.FK_stock) AS CountOfFK_stock
FROM stock INNER JOIN orderstock ON stock.stockID = orderstock.FK_stock
WHERE stock.perishable=0
GROUP BY orderstock.FK_orderNo
How do I access information relating to the FK_stock to make this work? When I attempt to combine information from the stock table to this end, each item of stock is counted separately.
Results from (1)
FK_OrderNo CountOfFK_Stock
9 10
104 8
105 3
106 10
107 8
108 10
109 11
110 9
Desired results (something like):
FK_OrderNo CountOfFK_Stock
9 7
104 8
105 3
106 4
107 7
108 2
109 11
110 6
I guess you are looking for conditional count
Move the where clause filter to Count Aggregate and make the count aggregate to count the record only when stock.perishable = 0.
SELECT orderdemo.fk_orderno,
Count(CASE
WHEN stock.perishable = 0 THEN 1
END) AS nonperishable_count
FROM stock
INNER JOIN orderdemo
ON stock.studentid = orderdemo.fk_stock
GROUP BY orderdemo.fk_orderno
Count Aggregate can be replaced by SUM aggregate as well. Something like this
Sum(CASE
WHEN stock.perishable = 0 THEN 1
ELSE 0
END) AS nonperishable_count

Mysql best students in every class in a school

In MySql I need to select top student in every class in a school in termid=10 to get discount for next term enrollment .
Please notice that total is not in table(I put in below for clearing problem)
I have this workbook table for all students workbook:
id studentid classid exam1 exam2 total termid
1 2 11 20 40 60 10
2 1 22 40 20 60 10
3 4 11 40 20 60 10
4 5 33 10 60 70 10
5 7 22 10 40 50 10
6 8 11 10 30 40 10
7 9 33 20 45 65 10
8 11 11 null null null 10
9 12 54 null null null 02
10 13 58 null null null 02
1st challenge is : exam1 and exam2 are VARCHAR and total is not in table (as i explained).
2nd challenge is : as you can see in id=8 std #11 has not numbers
3rd challenge is : may be two students have top level so they must be in result.
I need result as :
id studentid classid exam1 exam2 total termid
1 2 11 20 40 60 10
3 4 11 40 20 60 10
4 5 33 10 60 70 10
2 1 22 40 20 60 10
i have this query but not work good as i mention.
SELECT DISTINCT id,studentid,classid,exam1,exam2,total,termid ,(CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2))) FROM workbook WHERE ClassId = '10';
You can get the total for the students by just adding the values (MySQL will convert the values to numbers). The following gets the max total for each class:
select w.classid, max(coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) as maxtotal
from workbook w
group by w.classid;
You can then join this back to the original data to get information about the best students:
select w.*, coalesce(w.exam1, 0) + coalesce(w.exam2, 0) as total
from workbook w join
(select w.classid, max(coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) as maxtotal
from workbook w
group by w.classid
) ww
on w.classid = ww.classid and (coalesce(w.exam1, 0) + coalesce(w.exam2, 0)) = ww.maxtotal;
Another approach is to join the table with itself. You find out the max for each class and then join all students of this class which match the class max:
max for each class (included in the final statement already):
SELECT classid, MAX(CAST(exam1 AS UNSIGNED) + CAST(exam2 AS UNSIGNED)) as 'maxtotal'
FROM students
WHERE NOT ISNULL(exam1)
AND NOT ISNULL(exam2)
GROUP BY classid
The complete statement:
SELECT s2.*, s1.maxtotal
FROM (SELECT classid, MAX(CAST(exam1 AS UNSIGNED) + CAST(exam2 AS UNSIGNED)) as 'maxtotal'
FROM students
WHERE NOT ISNULL(exam1)
AND NOT ISNULL(exam2)
GROUP BY classid) s1
JOIN students s2 ON s1.classid = s2.classid
WHERE s1.maxtotal = (CAST(s2.exam1 AS UNSIGNED) + CAST(s2.exam2 AS UNSIGNED));
SQL Fiddle: http://sqlfiddle.com/#!2/9f117/1
Use a simple Group by Statement:
SELECT
studentid,
classid,
max(coalesce(exam1,0)) as max_exam_1,
max(coalesce(exam2,0)) as max_exam_2,
sum(coalesce(exam1,0) + coalesce(exam2,0)) as sum_exam_total,
termid
FROM
workbook
WHERE
termid=10
GROUP BY
1,2
ORDER BY
5
Try something like this:
SELECT id,studentid,classid,exam1,exam2,(CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2))) AS total,termid FROM `workbook` WHERE ((CAST(exam1 AS DECIMAL(9,2))+CAST(exam2 AS DECIMAL(9,2)))) > 50
Thanks all my friends
I think combine between 2 answer in above is best :
SELECT s2.*, s1.maxtotal
FROM (SELECT ClassId, MAX(
coalesce(exam1,0)+
coalesce(exam2,0)
) as 'maxtotal'
FROM workbook
WHERE
(
termid = '11'
)
GROUP BY ClassId) s1
JOIN workbook s2 ON s1.ClassId = s2.ClassId
WHERE s1.maxtotal = (
coalesce(exam1,0)+
coalesce(exam2,0)
) AND (s1.maxtotal >'75');
last line is good for s1.maxtotal=0 (some times student scores have not be entered and all equals 0 so all will shown as best students) or some times we need minimum score (to enroll in next term).
So thanks all