Properly SQL query - mysql

I need to skip results with high price per day. I've got a table like this:
+------+-------------+-------+
| days | return_date | value |
+------+-------------+-------+
| 2 | 2017-12-27 | 15180 |
| 3 | 2017-12-28 | 14449 |
| 4 | 2017-12-29 | 13081 |
| 5 | 2017-12-30 | 11203 |
| 6 | 2017-12-31 | 9497 |
| 6 | 2017-12-31 | 9442 |
+------+-------------+-------+
How can I print only the lowest price for 6 days (9442 in this example).

We can use a GROUP BY clause and an aggregate function. For example:
SELECT t.days
, t.return_date
, MIN(t.value) AS min_value
FROM mytable t
GROUP
BY t.days
, t.return_date
This doesn't really "skip" rows. It accesses all the rows that satisfy the conditions in the WHERE clause (in this example, every row in the table). Then MySQL collapses rows into groups (in this example, rows with identical values of days and return_date get put into a group. The MIN(t.value) aggregate function selects out the minimum (lowest) value out of the group.
The query above is just an example of one approach of satisfying a particular specification.

Related

laravel group by date in join query to find sum of values

I am looking for laravel developer to solve a simple issue. I have 3 tables that I am joining to get data. Model data is like this:
date | order number | amount
I need to group by date and find the sum of amount. Like this:
date | order number | amount
12/06/2022 | ask20 | 150
12/06/2022 | ask20 | 50
13/06/2022 | ask21 | 120
15/06/2022 | ask20 | 110
15/06/2022 | ask23 | 10
16/06/2022 | ask20 | 30
Now, I need to group by date to get the value like this:
date | order number | amount
12/06/2022 | ask20 | 200 (added value)
13/06/2022 | ask21 | 120
15/06/2022 | ask20 | 110 (not added as the order number is different)
15/06/2022 | ask23 | 10
16/06/2022 | ask20 | 30
Remember, I am getting this data by joining 3 tables, Can anyone help solve this?
This seems a simple SUM function -
SELECT date, order_number, SUM(amount)
FROM <YOUR BIGGER QUERY..>
GROUP BY date, order_number

How to group and order dependent results by dates in MySQL

I have a database of units which have expiry dates. Some of the units are top level units and have sub-assemblies attached. Sub-assemblies also have expiry dates. I'm trying to create a listing that lists the top-level units ordered by their expiration dates and list all relevant sub-assemblies under them.
The ordering should be done in two different ways:
Query which lists top-level units and their sub-assemblies according to expiry date of the parent item.
Query which lists top-level units and their sub-assemblies ordered by the first expiry date of relevant sub-assembly under them.
Here's example of the master table:
ITEM | NAME | UID | INSTALLED_TO_UID | EXPIRY
AAA | Top_level_unit_1 | 1 | | 2018-03-06
BBB | Sub_assy_1 | 75 | 1 | 2019-06-11
AAA | Top_level_unit_2 | 2 | | 2018-08-12
CCC | Sub_assy_2 | 26 | 1 | 2020-02-05
DDD | Sub_assy_3 | 59 | 2 | 2019-11-11
EEE | Sub_assy_4 | 41 | 2 | 2019-10-30
FFF | Sub_assy_5 | 11 | 1 | 2018-04-10
I am running these now in nested queries (second query inside foreach loop), but i am sure there is better and more efficient way of doing this. At least for case 1.
SELECT *
FROM master
WHERE ITEM = AAA
ORDER
BY EXPIRY ASC
And inside result loop for each result:
SELECT *
FROM master
WHERE INSTALLED_TO_UID = (UID from parent query)
The result should be like:
ITEM | NAME | UID | INSTALLED_TO_UID | EXPIRY
AAA | Top_level_unit_1 | 1 | | 2018-03-06
BBB | Sub_assy_1 | 75 | 1 | 2019-06-11
CCC | Sub_assy_2 | 26 | 1 | 2020-02-05
FFF | Sub_assy_5 | 11 | 1 | 2018-04-10
AAA | Top_level_unit_2 | 2 | | 2018-08-12
DDD | Sub_assy_3 | 59 | 2 | 2019-11-11
EEE | Sub_assy_4 | 41 | 2 | 2019-10-30
For case 2 i don't have a clue yet...
Summary: I would like to have only one query to group and order the results instead of relying to another query in result loop. Secondly i would like to figure out a query to order the results by first expiring child item grouped by parent items
You need to use an outer self join to find the parent record (for query 1) or the first-of-children record (for query 2). Then you can use coalesce to decide which value to take (either from the joined table or the main one) for setting the order.
Query 1:
SELECT record.*
FROM master record
LEFT JOIN master parent
ON record.installed_to_uid = parent.uid
AND parent.installed_to_uid is null
ORDER BY COALESCE(parent.expiry, record.expiry),
COALESCE(parent.uid, record.uid),
COALESCE(record.installed_to_uid,-1),
record.expiry
Query 2:
SELECT record.*
FROM master record
LEFT JOIN (SELECT installed_to_uid, MIN(expiry) expiry
FROM master
WHERE installed_to_uid IS NOT NULL
GROUP BY installed_to_uid) first
ON COALESCE(record.installed_to_uid, record.uid) = first.installed_to_uid
ORDER BY COALESCE(first.expiry, record.expiry),
COALESCE(first.installed_to_uid, record.uid),
COALESCE(record.installed_to_uid,-1),
record.expiry
See it run on sqlfiddle
Although there are surely more efficient solutions, why not start with this:
SELECT *
FROM master
WHERE INSTALLED_TO_UID = (
SELECT UID
FROM master
WHERE ITEM = AAA
)
?

Only return an ordered subset of the rows from a joined table

Given a structure like this in a MySQL database
#data_table
(id) | user_id | time | (...)
#relations_table
(id) | user_id | user_coach_id | (...)
we can select all data_table rows belonging to a certain user_coach_id (let's say 1) with
SELECT rel.`user_coach_id`, dat.*
FROM `relations_table` rel
LEFT JOIN `data_table` dat ON rel.`uid` = dat.`uid`
WHERE rel.`user_coach_id` = 1
ORDER BY val.`time` DESC
returning something like
| user_coach_id | id | user_id | time | data1 | data2 | ...
| 1 | 9 | 4 | 15 | foo | bar | ...
| 1 | 7 | 3 | 12 | oof | rab | ...
| 1 | 6 | 4 | 11 | ofo | abr | ...
| 1 | 4 | 4 | 5 | foo | bra | ...
(And so on. Of course time are not integers in reality but to keep it simple.)
But now I would like to query (ideally) only up to an arbitrary number of rows from data_table per distinct user_id but still have those ordered (i.e. newest first). Is that even possible?
I know I can use GROUP BY user_id to only return 1 row per user, but then the ordering doesn't work and it seems kind of unpredictable which row will be in the result. I guess it's doable with a subquery, but I haven't figured it out yet.
To limit the number of rows in each GROUP is complicated. It is probably best done with an #variable to count, plus an outer query to throw out the rows beyond the limit.
My blog on Groupwise Max gives some hints of how to do such.

Multiple Columns with Duplicate Values

Currently, I have a MySQL table that has a few columns in it. The following is a sample of the table with data:
+----------+---------------------+
| hospt_id | file_id | clinic_id |
+----------+---------------------+
| 212837 | 9 | NULL |
| 123837 | 14 | 2134319 |
| 345567 | 9 | NULL |
| 123456 | 14 | 2134320 |
| 123456 | 14 | 2134320 |
+----------+---------------------+`
What I am trying to do is to write a query that will return all records where the three columns are repeate.
For example, the last two rows are repeated. So I would want to get those returned. I know how to do duplicate searches for a single column, but not sure how to do for multiple columns.
You just need to group by all three records to get a count of how many rows are in each group. You can then filter it down to those that have more than one matching row in the having clause.
select hospt_id, file_id, clinic_id, count(*)
from <table>
group by hospt_id, file_id, clinic_id
having count(*) > 1;
Here's a demo: http://sqlfiddle.com/#!9/91bf9/2

MySQL: optimize query for scoring calculation

I have a data table that I use to do some calculations. The resulting data set after calculations looks like:
+------------+-----------+------+----------+
| id_process | id_region | type | result |
+------------+-----------+------+----------+
| 1 | 4 | 1 | 65.2174 |
| 1 | 5 | 1 | 78.7419 |
| 1 | 6 | 1 | 95.2308 |
| 1 | 4 | 1 | 25.0000 |
| 1 | 7 | 1 | 100.0000 |
+------------+-----------+------+----------+
By other hand I have other table that contains a set of ranges that are used to classify the calculations results. The range tables looks like:
+----------+--------------+---------+
| id_level | start | end | status |
+----------+--------------+---------+
| 1 | 0 | 75 | Danger |
| 2 | 76 | 90 | Alert |
| 3 | 91 | 100 | Good |
+----------+--------------+---------+
I need to do a query that add the corresponding 'status' column to each value when do calculations. Currently, I can do that adding the following field to calculation query:
select
...,
...,
[math formula] as result,
(select status
from ranges r
where result between r.start and r.end) status
from ...
where ...
It works ok. But when I have a lot of rows (more than 200K), calculation query become slow.
My question is: there is some way to find that 'status' value without do that subquery?
Some one have worked on something similar before?
Thanks
Yes, you are looking for a subquery and join:
select s.*, r.status
from (select s.*
from <your query here>
) s left outer join
ranges r
on s.result between r.start and r.end
Explicit joins often optimize better than nested select. In this case, though, the ranges table seems pretty small, so this may not be the performance issue.