Group and order rows with multiple column MySQL - mysql

I want to rows according to same column value.
Suppose this is a table
id name topic
1 A t
2 B a
3 c t
4 d b
5 e b
6 f a
I want result something like this.
id name topic
1 A t
3 c t
2 B a
6 f a
4 d b
5 e b
As you can see these are not order by topic neither by id, it sort about that topic which come first if t come first sort t first, one second a come then sort according to a then b.
if you apply ORDER BY topic it sort a b t or in DESC t b a but required result is t a b
Any suggestion ?

DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,topic CHAR(1) NOT NULL
);
INSERT INTO my_table VALUES
(1,'t'),
(2,'a'),
(3,'t'),
(4,'b'),
(5,'b'),
(6,'a');
SELECT x.*
FROM my_table x
JOIN
( SELECT topic, MIN(id) id FROM my_table GROUP BY topic ) y
ON y.topic = x.topic
ORDER
BY y.id,x.id;
+----+-------+
| id | topic |
+----+-------+
| 1 | t |
| 3 | t |
| 2 | a |
| 6 | a |
| 4 | b |
| 5 | b |
+----+-------+

You can use CASE expression in ORDER BY.
Query
select * from `your_table_name`
order by
case `topic`
when 't' then 1
when 'a' then 2
when 'b' then 3
else 4 end
, `name`;

Related

mysql select number changes union two tables

table a shows user current selecting item (contains: selected_date, item_id)
which is added after user selected a item
table b shows user unselected history (contains: selected_date, unselected_date, item_id)
which is added after user unselected a item
I want to show the selected count changes of all items between date r and y
just like:
item_id | change
1 | +1
2 | -3
3 | +10
tables:
create table a (
user_id int,
item_id int ,
selected_date date,
)
create table b (
item_id int,
selected_date date,
unselected_date date,
primary key(item_id, selected_date)
);
sample data:
a:
+-------+--------------+
|item_id|selected_date |
+-------+--------------+
| 48|2022-03-17 |
| 49|2022-03-17 |
+-------+--------------+
b:
+-------+--------------+---------------+
|item_id|selected_date |unselected_date|
+-------+--------------+---------------+
| 47|2022-03-14 |2022-03-17 |
| 48|2020-03-11 |2022-03-12 |
| 49|2021-03-17 |2022-03-14 |
+-------+--------------+---------------+
Output:
if r = 2022-03-15, y = 2022-03-17
+-------+------+
|item_id|change|
+-------+------+
| 47|-1 |
| 48|+2 |
| 49|+1 |
+-------+------+
Ok, I should explain my problem more detail.
I need to union two query of table a and b into:
select (date), (sum of changes)
a means changes +1
and b means changes -1
Therefore, if day y has 4 rows of a and 2 rows of b. (selected_date = y)
It should get:
+----+-------+
|date|changes|
+----+-------+
| y|+2 |
+----+-------+
My solution
As jiwopene said, I mix table a and b into one table.
It has selected_date and nullable unselected_date.
Finally, get the sum of changes using case
select date, sum(num) from (
select a.selected_date as date,
case
when a.unselected_date is null then 1
else -1
end as num
from a
) as sq
group by date;

Selecting one distinct row from group of entries

ID TYPE DATE
1 A 01/02/2019
1 B 01/21/2019
1 C 02/03/2019
2 A 01/04/2019
2 C 01/29/2019
3 A 01/14/2019
3 B 03/11/2019
So using the table above as an example what I'm trying to do with a similar table is extract a specific Type from each Distinct ID. So let's say I only want to Select Type B from each Distinct ID. Now, the big confusion starts for me when I incorporate the Date. I want to Select Type B from each ID but only if Type B is the most recent date.
So in this instance the only row with Type B as the last date would be the last row.
3 B 03/11/2019
Any suggestions as to what my query should look like?
With NOT EXISTS:
select t.* from tablename t
where t.type = 'B'
and not exists (
select 1 from tablename
where id = t.id and date > t.date
)
See the demo.
Results:
| ID | TYPE | DATE |
| --- | ---- | ------------------- |
| 3 | B | 2019-03-11 00:00:00 |

Efficient way to get DISTINCT rows of Table A when JOINing with Table B

Simple problem. Given example tables:
Table A:
id | type
---+-----
1 | A
2 | B
3 | C
Table B:
id | a_id | type
---+------+-----
1 | 1 | X
2 | 2 | Y
3 | 1 | X
4 | 3 | Z
(there are additional columns, which I omitted, in order to clarify the problem)
The query:
SELECT a.*
FROM a a
INNER JOIN b b ON b.a_id = a.id
WHERE b.type = 'X'
Result:
id | type
---+-----
1 | A
1 | A
SQL Fiddle: http://sqlfiddle.com/#!2/e6138f/1
But I only want to have distinct rows of Table A. I know, I could do SELECT DISTINCT a.*, but our Table A has about 40 columns, and this SELECT can return 100-10000 rows. Isn't that extremely slow, if the database has to compare each column?
Or is MySQL intelligent enough, to just focus on the Primary Key for the DISTINCT operation?
Thanks in advance :)
Use exists instead of an explicit join:
select a.*
from tablea a
where exists (select 1 from tableb b where b.a_id = a.id and b.type = 'x');
For performance, create an index on tableb(a_id, type).

Can grouped expressions be used with variable assignments?

I'm trying to calculate row differences (like MySQL difference between two rows of a SELECT Statement) over a grouped result set:
create table test (i int not null auto_increment, a int, b int, primary key (i));
insert into test (a,b) value (1,1),(1,2),(2,4),(2,8);
Gives
| a | b
---------
| 1 | 1
| 1 | 2
| 2 | 4
| 2 | 8
This is the simple SQL with group and max(group) result columns:
select
data.a,
max(data.b)
from
(
select a, b
from test
order by i
) as data
group by a
order by a
The obvious result is
| a | max(data.b)
-----------------
| 1 | 2
| 2 | 8
Where I'm failing is when I want to calculate the row-by-row differences on the grouped column:
set #c:=0;
select
data.a,
max(data.b),
#c:=max(data.b)-#c
from
(
select a, b
from test
order by i
) as data
group by a
order by a
Still gives:
| a | max(data.b) | #c:=max(data.b)-#c
--------------------------------------
| 1 | 2 | 2 (expected 2-0=2)
| 2 | 8 | 8 (expected 8-2=6)
Could anybody highlight why the #c variable is not updating from grouped row to grouped row as expected?
SELECT data.a
, data.b
, #c := data.b - #c
FROM (
SELECT a
, max(b) AS b
FROM test
GROUP BY a
) AS data
ORDER BY a
Example
The 'documented' solution might look like this...
SELECT x.*
, #c := b - #c c
FROM test x
JOIN
( SELECT a,MAX(b) max_b FROM test GROUP BY a ) y
ON y.a = x.a
AND y.max_b = x.b
JOIN (SELECT #c:= 0) vals;

How to find the exact group that matches to defined values?

I simplified the table so it is easier to understand.
I have a table with groups and a group exists of multiple values. Here is the table:
VALUE | GROUP
A | 1
B | 1
A | 2
C | 2
B | 3
A | 4
B | 4
A | 5
B | 5
C | 5
I want to give values to my query wich I programmatically build and find the exact group that matches to these values.
For example if I give value A and B to my query I want as a result group 1 and 4
A ---------------> null
A and B ----------> 1 and 4
A , B and C ------> 5
B ---------------> 3
A and C ----------> 2
C ----------------> null
You can use a query like the following (assuming value,group pairs unique):
select `GROUP`
from MyTable
group by `GROUP`
having count(`VALUE`) = count(case when `VALUE` IN ('a','b') then 1 end)
and count(case when `VALUE` IN ('a','b') then 1 end) = #Count;
Where ('a','b') would be the list of values you are testing for, and #Count would be the count of different values in your check set (2 in this case).
Demo: http://www.sqlfiddle.com/#!2/78def/13