Remove rows with top and bottom x, y distinct values - mysql

I have the following table ordered by val. I would like to remove all rows that share the top and bottom x and y distinct values in the source column.
If x is 1 and y is 2, then on this table:
val
source
1
1
2
3
3
1
3
2
4
4
5
3
7
4
7
5
9
5
The result should be:
val
source
2
3
3
2
5
3
Where 2 rows was were removed because the top row source = 1 and 4 rows were removed because the bottom 2 distinct values in source was 4 and 5.
How could I achieve this result?

WITH
cte1 AS (
SELECT val,
source,
COALESCE(source <> LAG(source) OVER (ORDER BY val), 1) like_prev,
COALESCE(source <> LEAD(source) OVER (ORDER BY val), 1) like_next
FROM test
),
cte2 AS (
SELECT val,
source,
SUM(like_prev) OVER (ORDER BY val) sum_prev,
SUM(like_next) OVER (ORDER BY val DESC) sum_next
FROM cte1
)
DELETE test
FROM test
JOIN cte2 USING (source)
WHERE cte2.sum_prev <= #x
OR cte2.sum_next <= #y;
https://dbfiddle.uk/1bRF9BpU (the values in val are made unique).

Related

MySQL query for selecting multiple rows as arrays of those rows data

Is there a way to select frist 3 rows and after that next 3 ( offset 3 ) and get the result as two arrays in a single query ? Something like:
(SELECT * FROM product WHERE 1 LIMIT 3) as first_array
(SELECT * FROM product WHERE 1 OFFSET 3 LIMIT 3) as second_array
Hope you understand me. Sorry about the explanation just dont't know how to explain in other way.
Lets say I only want the ids - output example:
id_1 id_2
1 4
2 5
3 6
What I have tried from the answers below the post is :
SELECT id as id_1 FROM `ct_product` WHERE 1 LIMIT 3
UNION ALL
SELECT id as id_2 FROM `ct_product` WHERE 1 LIMIT 3 OFFSET 3
The result is strange for me. It seems it returns only the second query results and they are not the 4th 5th and 6th row but the 5th 6th and 3th (in this order).
My table rows are:
id
1
2
3
4
5
6
7
You could do it with this query:
SELECT a1.id, a2.id
FROM (SELECT *, #rownum1:=#rownum1+1 AS rownum
FROM (SELECT id
FROM `ct_product`
LIMIT 3
) art
JOIN (SELECT #rownum1 := 0) r
) a1
JOIN (SELECT *, #rownum2:=#rownum2+1 AS rownum
FROM (SELECT id
FROM `ct_product`
LIMIT 3, 3
) art
JOIN (SELECT #rownum2 := 0) r
) a2
ON a1.rownum = a2.rownum
Output:
id id
1 4
2 5
3 6
This query works by creating two new tables with artificially generated row numbers (#rownum1 and #rownum2) from the first 3 and the second 3 rows in the original table. They are then JOINed on matching row numbers to get the desired result.

MySQL query with GROUP BY, SUM, MAX and subquery

I have the following tabel structure:
Id Num1 Num2 Type Num3
1 2 2 1 4
1 3 1 2 5
1 1 1 3 2
2 2 1 1 3
2 0 1 2 2
2 4 3 3 6
I need a query with group by 'Id', sum of 'Num1', sum of 'Num2', max of 'Num3' and the 'Type' related to the MAX of 'Num3'.
So, the desired output is:
Id Sum(Num1) Sum(Num2) type Max(Num3)
1 6 4 2 5
2 6 4 3 6
Without this related 'Type' the query below works fine:
SELECT
Id,
SUM(Num1),
SUM(Num2),
MAX(Num3)
GROUP BY
Id
I tried different methods of subquery but can't make it work yet.
Your problem is a bit of a spin on the greatest value per group problem. In this case, we can use a subquery to find the max Num3 value for each Id. But, in the same subquery we also compute the sum aggregates.
SELECT
t1.Id,
t2.s1,
t2.s2,
t1.Type,
t1.Num3
FROM yourTable t1
INNER JOIN
(
SELECT Id, SUM(Num1) AS s1, SUM(Num2) AS s2, MAX(Num3) AS m3
FROM yourTable
GROUP BY Id
) t2
ON t1.Id = t2.Id AND t1.Num3 = t2.m3;
As a hat tip to MySQL 8+, and to ward off evil spirits, we can also write a query using analytic functions:
SELECT Id, s1, s2, Type, Num3
FROM
(
SELECT
Id,
SUM(Num1) OVER (PARTITION BY Id) s1,
SUM(Num2) OVER (PARTITION BY Id) s2,
Type,
Num3,
MAX(Num3) OVER (PARTITION BY Id) m3
FROM yourTable
) t
WHERE Num3 = m3;

SQL: How to count items in a specific ordernation

This is my table:
PACKAGE_ID ITEM_ID
1 1
1 2
1 3
2 4
2 5
3 6
4 7
4 8
I want a new column called count that count 1 to N according to package ID. E.g.:
PACKAGE_ID ITEM_ID COUNT
1 1 1
1 2 2
1 3 3
2 4 1
2 5 2
3 6 1
4 7 1
4 8 2
Thanks!
p.s: I'm using MariaDb 10.1
You can use window function.
SELECT PACKAGE_ID, ITEM_ID
, ROW_NUMBER() OVER (PARTITION BY PACKAGE_ID ORDER BY PACKAGE_ID, ITEM_ID) AS THE_COUNT
FROM your_table
In pre 8.0 MySQL, the fastest method is probably variables:
select t.*,
(#rn := if(#p = package_id, #rn + 1,
if(#p := package_id, 1, 1)
)
) as counter
from t cross join
(select #rn := 0, #p := -1) params
order by package_id, item_id;
Obviously an index on (package_id, item_id) would benefit this query.
In MySQL 8+ or similar versions of MariaDB, use row_number():
You can use subquery :
select *, (select count(1)
from table t1
where t1.PACKAGE_ID = t.PACKAGE_ID and
t1.ITEM_ID <= t.ITEM_ID
) as COUNT
from table t;

Find every 2 alternate records in sql

I have an issue with a SQl Query. It's as below.
In a particular field the record consists of "A" and "B" only.
Now if I want to find 2 records of "A" followed by 2 records of "B" and then again 2 Records of "A" and 2 records of "B" and so on till the end of records.
example output should be something like below.
ID Field
--------- -----
2 A
3 A
1 B
5 B
4 A
7 A
6 B
8 B
.........
.........
.............. and so on
Kindly help me with the above....as am stuck for this query.
Thanks!
You can do this by enumerating the rows for each value and then cleverly ordering the results:
select id, field
from (select t.*,
if(field = 'A', #a_rn := #a_rn + 1, #b_rn := #b_rn + 1) as rn
from table t
(select #a_rn := 0, #b_rn := 0) vars
) t
order by (rn - 1) div 2, field;
or slower but cooler...
SELECT x.*
FROM my_table x
JOIN my_table y
ON y.field = x.field
AND y.id <= x.id
GROUP
BY field
, id
ORDER
BY CEILING(COUNT(*)/2)
, field;

How to compare and change values in MYSQL

My table looks like:
[Number] [Value1]
1234567 8
1234567C 7
9876543 1
9876543C 2
5555555 3
5555555C 3
I want to search the entries for same values in the first column (except the "C" in the end of the number) and set the higher value in the second column to the lower one.
There are always only two same values (one with "C") and some pairs have same values in the second column and some have different.
The result of the query should be:
Number Value1
1234567 7
1234567C 7
9876543 1
9876543C 1
5555555 3
5555555C 3
The following is not an ideal solution but should do what you want:
update yourTable
set value1 = (
select min(value1) from (
select * from yourTable
) as x
where yourTable.number = x.number + 'C');
I have tested it with this in mysql workbench:
create table yourTable(number varchar (10),value1 int);
insert into yourTable Values('1234567',8);
insert into yourTable Values('1234567C',7);
insert into yourTable Values('9876543',1);
insert into yourTable Values('9876543C',2);
insert into yourTable Values('5555555',3);
insert into yourTable Values('5555555C',3);
insert into yourTable Values('55555556',10);
insert into yourTable Values('55555556C',2);
Then select * from yourTable;will return:
1234567 8
1234567C 7
9876543 1
9876543C 2
5555555 3
5555555C 3
55555556 10
55555556C 2
After the update select * from yourTable; will return:
1234567 7
1234567C 7
9876543 1
9876543C 1
5555555 3
5555555C 3
55555556 2
55555556C 2
Hope that is what you wanted :)
Actually, you don't need any checking, since there are only 2 values (and thus the query is even simpler):
UPDATE
table
SET
Value1 =
(
SELECT
MAX(Value1)
FROM
table t
WHERE
table.Number = t.Number
OR table.Number = t.Number + 'C'
)
WHERE
RIGHT(Number, 1) != 'C'