How to ORDER all columns FIRST in mysql tableTHEN limit - mysql

Take an example of a table (tblabc) with the following data
id amount
1 10
2 20
3 50
4 30
5 90
6 80
7 70
8 40
9 100
10 60
Normally, if you apply the code SELECT * FROM tblabc ORDER BY `amount` DESC LIMIT 0,2 it will return its result based on the first 2 columns only, which is not my desired result
What it will return
id amount
1 20
2 10
You can clearly see that the result is only from the first 2 columns instead of the whole table, sorted.
My desired result would be:
id amount
9 100
5 90
Also how the method applied should be consistent with the pagination i.e at the second phase SELECT * FROM tblabc ORDER BY `amount` DESC LIMIT 2,2 it should produce
id amount
6 80
7 70

Your desired results are wrong.
But you need only to order it by amount DESC
CREATE TABLE table1 (
`id` INTEGER,
`amount` INTEGER
);
INSERT INTO table1
(`id`, `amount`)
VALUES
('1', '10'),
('2', '20'),
('3', '50'),
('4', '30'),
('5', '90'),
('6', '80'),
('7', '70'),
('8', '40'),
('9', '100'),
('10', '60');
SELECT * FROM table1 ORDER BY `amount` DESC LIMIT 0,2
id | amount
-: | -----:
9 | 100
5 | 90
SELECT * FROM table1 ORDER BY `amount` DESC LIMIT 2,2
id | amount
-: | -----:
6 | 80
7 | 70
db<>fiddle here

The query should be “select * from tableabc ORDER BY amount” not just BY. That wouldnt even work in MySQL.

Related

Divide the sum of each group by the grand total

I want to get the sum of group A and B separately, and divide each by the total sum.
I tried to use this:
select name, sum(qt)
from ntbl
group by name
order_id
name
qt
1
A
12
2
A
20
3
B
33
4
B
45
Result should be as:
name
qt
dv
A
32
0.29
B
78
0.70
You can combine aggregate and window functions together:
select name
, sum(qt) as sum_qt
, sum(qt) / sum(sum(qt)) over () * 100 as pct_qt
from t
group by name
You can crossjoin another subquery that sums up all quantities
CREATE TABLE ntbl (
`order_id` INTEGER,
`name` VARCHAR(1),
`qt` INTEGER
);
INSERT INTO ntbl
(`order_id`, `name`, `qt`)
VALUES
('1', 'A', '12'),
('2', 'A', '20'),
('3', 'B', '33'),
('4', 'B', '45');
SELECT name, sum_name, ROUND(sum_name/sum_qt,2) as dv
FROM
(select name,SUM(qt) sum_name from ntbl group by name) q1 CROSS JOIN (SELECT SUM(`qt`) sum_qt FROM ntbl) q2
name | sum_name | dv
:--- | -------: | ---:
A | 32 | 0.29
B | 78 | 0.71
db<>fiddle here

Min and Max on a SUM column

I have a table like:
Phrase | qty
phrase_1 | 4
phrase_1 | 1
phrase_1 | 8
phrase_2 | 2
phrase_3 | 3
phrase_3 | 2
What I initially return is:
phrase_1 | 13
phrase_3 | 5
phrase_2 | 2
Using:
SELECT phrase, sum(qty) as total
FROM mytable
GROUP By phrase
ORDER BY total DESC
What I need, and can't figure out, is how to return the min and max with the results.
so I would get:
phrase, qty, min, max
phrase_1 | 13 | 2 | 13
phrase_3 | 5 | 2 | 13
phrase_2 | 2 | 2 | 13
Because I want to run a normalization on the resultset and return a new order based on values between 1 and 0.
Something like (this doesn't work):
SELECT phrase, sum(qty) as total, (total - min(total)/max(total) - min(total)) AS rank
FROM mytable
GROUP By phrase
ORDER BY rank DESC
The above statement is ultimiately what I'm looking to do and not sure if it's possible.
With some subqueries you can achieve your goal, but pretty it will never get
CREATE TABLE mytable (
`Phrase` VARCHAR(8),
`qty` INTEGER
);
INSERT INTO mytable
(`Phrase`, `qty`)
VALUES
('phrase_1', '4'),
('phrase_1', '1'),
('phrase_1', '8'),
('phrase_2', '2'),
('phrase_3', '3'),
('phrase_3', '2');
SELECT phrase,total,(total - mi/ma - mi) AS rank
FROM
(SELECT phrase, sum(qty) as total
FROM mytable
GROUP By phrase
ORDER BY total DESC) t1 CROSS JOIN (SELECT MIN(total) mi,MAX(total) ma
FROM
(SELECT phrase, sum(qty) as total
FROM mytable
GROUP By phrase
ORDER BY total DESC) t1) t2
phrase | total | rank
:------- | ----: | ------:
phrase_1 | 13 | 10.8462
phrase_3 | 5 | 2.8462
phrase_2 | 2 | -0.1538
db<>fiddle here
You want window functions:
SELECT phrase, sum(qty) as total,
MIN(SUM(qty)) OVER () as min_total,
MAX(SUM(qty)) OVER () as max_total
FROM mytable
GROUP By phrase
ORDER BY total DESC
You can use code below :
SELECT phrase, sum(qty) as total,
MIN(SUM(qty)) OVER () as min_total,
MAX(SUM(qty)) OVER () as max_total
into #temp
FROM mytable
GROUP By phrase
ORDER BY total DESC
Select *,(total - min_total/max_total - min_total) AS rank From #temp
Drop Table #temp

Select by a limited number of distinct values

I am trying to chunkage selects by distinct values of one column. Like give me all rows for the first five distinct values. Or give me all rows for next five distinct values of one column.
I have a table like this VBS_DOCUMENT:
PK T_DOCUMENT
1 1
2 1
3 1
4 3
5 3
6 3
7 5
8 5
9 6
10 7
SELECT * FROM VBT_DOCUMENT
WHERE T_DOCUMENT IN (SELECT DISTINCT T_DOCUMENT FROM VBT_DOCUMENT LIMIT 2 OFFSET 2);
But then I get this error:
1235 - This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
PK T_DOCUMENT
7 5
8 5
9 6
Use your subquery in the FROM clause and join it with the base table:
SELECT t.*
FROM (
SELECT DISTINCT T_DOCUMENT
FROM VBT_DOCUMENT
ORDER BY T_DOCUMENT
LIMIT 2 OFFSET 2
) x
JOIN VBT_DOCUMENT t on t.T_DOCUMENT = x.T_DOCUMENT
Result:
| PK | T_DOCUMENT |
| --- | ---------- |
| 7 | 5 |
| 8 | 5 |
| 9 | 6 |
View on DB Fiddle
Note: When you use LIMIT you should also define a deterministic ORDER BY clause. Otherwise you might get an unexpected result depending on the execution plan.
WITH can be used in MySQL8.0 (,or MariaDB 10.2.1 )
WITH abc as (SELECT DISTINCT T_DOCUMENT FROM VBS_DOCUMENT LIMIT 2 OFFSET 2)
SELECT * FROM VBS_DOCUMENT WHERE T_DOCUMENT IN (select * from abc);
output:
# pk, t_document
'7', '5'
'8', '5'
'9', '6'

How to get biggest differences in column compared to last month?

I have two tables - Products and Prices
Every month table Prices is populated with new prices for each products. How can I get 5 products whose prices have the biggest incremental difference from last month prices?
table Products
id | name
1 | apples
2 | pears
3 | bananas
table Prices
id | price | product_id | created_at
1 | 10 | 1 | 2017-02-07 07:00:00
2 | 10 | 2 | 2017-02-07 07:00:00
3 | 15 | 3 | 2017-02-07 07:00:00
5 | 15 | 1 | 2017-03-07 07:00:00
6 | 20 | 2 | 2017-03-07 07:00:00
7 | 25 | 3 | 2017-03-07 07:00:00
The result would be to find out that
1. Bananas has prices by 15 higher (lastMonth: 15, now: 25)
2. Pears 2 has prices by 10 higher (lastMonth: 10, now: 20)
3. Apples has prices by 5 higher (lastMonth: 10, now: 15)
I was thinking something like this (uff I know this is terrible)
SELECT products.id, products.name, prices.beforePrice, prices.afterPrice, prices.difference
FROM products
INNER JOIN prices ON products.id = prices.product_id
WHERE
(
SELECT *biggest-difference*
FROM prices
WHERE *difference_between_last_2_months*
GROUP BY product_id
LIMIT 5
)
Create table/insert data
CREATE TABLE Products
(`id` INT, `name` VARCHAR(7))
;
INSERT INTO Products
(`id`, `name`)
VALUES
(1, 'apples'),
(2, 'pears'),
(3, 'bananas')
;
CREATE TABLE Prices
(`id` INT, `price` INT, `product_id` INT, `created_at` DATETIME)
;
INSERT INTO Prices
(`id`, `price`, `product_id`, `created_at`)
VALUES
(1, 10, 1, '2017-02-07 07:00:00'),
(2, 10, 2, '2017-02-07 07:00:00'),
(3, 15, 3, '2017-02-07 07:00:00'),
(5, 15, 1, '2017-03-07 07:00:00'),
(6, 20, 2, '2017-03-07 07:00:00'),
(7, 25, 3, '2017-03-07 07:00:00')
;
Query
SELECT
Products.id
, Products.name
, (current_month.price - last_month.price) AS difference
, (
CASE
WHEN last_month.price > current_month.price
THEN 'lower'
WHEN last_month.price < current_month.price
THEN 'higher'
END
) AS incremental
, last_month.price 'lastMonth'
, current_month.price 'now'
FROM (
SELECT
*
FROM
Prices
WHERE
MONTH(created_at) = MONTH((CURDATE() - INTERVAL 1 MONTH))
)
AS
last_month
INNER JOIN (
SELECT
*
FROM
Prices
WHERE
MONTH(created_at) = MONTH((CURDATE()))
)
AS
current_month
ON
last_month.product_id = current_month.product_id
INNER JOIN
Products
ON
last_month.product_id = Products.id
WHERE
last_month.price < current_month.price #incremental should be higher
ORDER BY
difference DESC
LIMIT 5
Result
id name difference incremental lastMonth now
------ ------- ---------- ----------- --------- --------
2 pears 10 higher 10 20
3 bananas 10 higher 15 25
1 apples 5 higher 10 15
You can use a proper joins based on fliterd select by month.
This should return the value you need ( you can add the literal string you need )
select p.name, m1.price as this_month, m2.price as prev_month, m2.price-m1.price as diff
from product
left join (
select price, product_id
from Prices
where month(created_at) = month(NOW())
and year(created_at) = year(NOW())
) m1 on m1.product_id = p.id
left join (
select price, product_id
from Prices
where month(created_at) = MONT(DATE_SUB(NOW(), INTERVAL 1 MONTH)
and year(created_at) = year(DATE_SUB(NOW(), INTERVAL 1 MONTH)
) m2 on m2.product_id = p.id
order by diff desc
limit 5

Return last matched row in mysql

I have this table
id | qty_from | qty_to | p_id | price
--------------------------------------
1 | 1 | 10 | 4 | 1000
--------------------------------------
2 | 11 | 20 | 4 | 2000
--------------------------------------
3 | 1 | 10 | 5 | 500
--------------------------------------
4 | 11 | 20 | 5 | 1000
--------------------------------------
5 | 1 | 10 | 6 | 1000
--------------------------------------
6 | 10 | 15 | 6 | 2000
And i tried to get rows by qty_from AND qty_to AND p_id using below code
SELECT * FROM table WHERE p_id IN ('4', '5', '6') AND qty_from <= 8 AND qty_to >= 8
It returns 1st, 3rd and 5th rows.
And when i use this code, it return only 1st and 3rd rows.
SELECT * FROM table WHERE p_id IN ('4', '5', '6') AND qty_from <= 16 AND qty_to >= 16
I want to return 6th row too. because it's the biggest qty in p_id = 6
How can i achieve this?
After discuss here is what you need (first and not tested solution) :
SELECT *
FROM (SELECT *, MAX(qty_to) as max_qty_to FROM `limit`) T
WHERE p_id IN ('4', '5', '6')
AND ( ( qty_from <= 16 AND qty_to >= 16 ) OR qty_to = T.max_qty_to )
You should update your where condition to achieve your desired result. The updated query will be -
SELECT * FROM table WHERE p_id IN ('4', '5', '6') AND qty_from <= 10 AND qty_to >= 8
I just solve this.
Thanks to #Meloman for giving me the clue.
SELECT * FROM table WHERE p_id IN ('4', '5', '6') AND ((qty_from <= 16 AND qty_to >= 16) OR (qty_to, p_id) IN (SELECT dd.qty_to, dd.p_id FROM table dd INNER JOIN (SELECT MAX(qty_to) AS mm, p_id FROM table GROUP BY p_id) ddd ON dd.p_id = ddd.p_id AND dd.qty_to = ddd.mm WHERE dd.p_id IN ('4', '5', '6'))) GROUP BY p_id
I test this multiple times and i think it's the answer
This solution works:
SELECT DISTINCT t3.*
FROM `table` t1
LEFT JOIN `table` t2 ON t2.id=IFNULL(
(SELECT id FROM `table` t3
WHERE t3.p_id=t1.p_id AND 16 BETWEEN t3.qty_from AND t3.qty_to
ORDER BY qty_to DESC LIMIT 1),
(SELECT id FROM `table` t4
WHERE t4.p_id=t1.p_id
ORDER BY qty_to DESC LIMIT 1))
WHERE t1.p_id IN ('4', '5', '6')
Basically, first a table with the given p_ids is fetched and then joined with the rows of the most desired ids per p_id (if 16 is not in range of qty_from and qty_to, the one with the biggest qty_to is taken).
One flaw: If multiple rows match the range condition, only the one with the biggest qty_to is selected, so there is only one result per p_id. I hope that's sufficient for you!