Sort mysql result by two column, but with a “holed” column - mysql

I have the following initial situation:
+------------+-------------+
| legacyRank | forcedRank |
+------------+-------------+
| 0 | NULL |
| 1 | 6 |
| 2 | NULL |
| 3 | 1 |
| 4 | NULL |
| 5 | NULL |
| 6 | 2 |
+------------+-------------+
You could generate this table by the following schema:
CREATE TABLE two_column_order (
legacyRank VARCHAR(45),
forcedRank VARCHAR(45)
);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (5, NULL);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (6, 2);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (7, NULL);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (0, NULL);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (1, NULL);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (2, 6);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (3, NULL);
INSERT INTO two_column_order (legacyRank, forcedRank)
VALUES (4, 1);
SELECT * FROM two_column_order
order by
CASE when `forcedRank` <> NULL THEN `forcedRank`
ELSE `legacyRank`
END
The goal is to put each line with no-NULL forcedRank column in the accurate position mentioned in this forcedRank column. The expected rendering is like:
+------------+-------------+
| legacyRank | forcedRank |
+------------+-------------+
0 | 0 | NULL |
1 | 3 | 1 |
2 | 6 | 2 |
3 | 2 | NULL |
4 | 4 | NULL |
5 | 5 | NULL |
6 | 6 | 6 |
+------------+-------------+
As you see, each line take the position ordered by the forcedRank column if not NULL. When the the NULL rows still sorted by the legacyRank column in the positions leaved unoccupied by the non-NULL rows, but never shift the forced rows.
In this order, I tried to use the CASE WHEN syntax inside the ORDER BY like this:
SELECT * FROM two_column_order
order by
CASE WHEN (`forcedRank` is NULL ) THEN `legacyRank`
END ,
-`forcedRank` DESC,
`legacyRank`
But the result doesn’t really feat my expectations:
+------------+-------------+
| legacyRank | forcedRank |
+------------+-------------+
| 3 | 1 |
| 6 | 2 |
| 6 | 6 |
| 0 | NULL |
| 2 | NULL |
| 4 | NULL |
| 5 | NULL |
+------------+-------------+
So how can I make the legacyRank column get order beyond the forcedrank rows without shift them?

NULL can't tbe comapred like that you need to use ISor in your case IS NOT
SELECT * FROM two_column_order
order by
CASE when `forcedRank` IS NOT NULL THEN `forcedRank`
ELSE `legacyRank`
END
legacyRank
forcedRank
0
null
1
null
4
1
6
2
3
null
5
null
2
6
7
null
fiddle
As the first answer won't give you the correct answer.
i have changed the order by adding a decimal point to the original number so that it will be bigger than the new forced rank.
it will keep the order and a the forced number is smaller then the legayrank, it get you follwoing result
SELECT * FROM two_column_order
order by
CASE when `forcedRank` IS NOT NULL THEN `forcedRank`
ELSE `legacyRank` + .1
END
legacyRank
forcedRank
0
null
4
1
1
null
6
2
3
null
5
null
2
6
7
null
fiddle

Related

Selectively ignoring tuples from a table

The problem that we are tackling with a data mining application is best described with an illustrative example.
There is a sample table myTable, which is defined as follows:
CREATE TABLE myTable
(
id INT UNSIGNED AUTO_INCREMENT,
colA VARCHAR(8),
colB VARCHAR(12),
revFlag CHAR(8), -- 'REVISED' or any other value, including NULL
PRIMARY KEY(id)
);
Any tuple with a revFlag value of REVISED takes precedence over any other tuple with the same value for colA, as long as the revFlag value of the latter tuple is not REVISED. In other words when we select rows from the table we skip all rows for which the revFlag value is not REVISED and there exists a row with the same value for colA for which the revFlag value is REVISED.
We populate the table as follows:
INSERT INTO myTable(colA, colB) VALUES ('XSR0KA3V', 'OLD-O7RAR81X'),
('4F2JG71O', 'OLD-E71BE63L'), ('MML3HN48', 'OLD-B02PFB63'),
('5H0MWVSB', 'OLD-V70XLGHT'), ('JW73ZX0J', 'OLD-KME1GXQF'),
('XZV0EY0G', 'OLD-N06BURDF'), ('9HBQZ88V', 'OLD-76HSPUAL'),
('YI5AT6G4', 'OLD-X8KAWD7Z');
INSERT INTO myTable(colA, colB, revFlag) VALUES
('XSR0KA3V', 'NEW-O7RAR81X', 'REVISED'),
('MML3HN48', 'NEW-B02PFB63', 'REVISED'),
('9HBQZ88V', 'NEW-76HSPUAL', 'REVISED'),
('YI5AT6G4', 'NEW-X8KAWD7Z', 'XYZ'),
('Z8H2B5KY', '3RINJV0K', 'REVISED');
Naturally SELECT * FROM myTable yields the following:
+----+----------+--------------+---------+
| id | colA | colB | revFlag |
+----+----------+--------------+---------+
| 1 | XSR0KA3V | OLD-O7RAR81X | NULL |
| 2 | 4F2JG71O | OLD-E71BE63L | NULL |
| 3 | MML3HN48 | OLD-B02PFB63 | NULL |
| 4 | 5H0MWVSB | OLD-V70XLGHT | NULL |
| 5 | JW73ZX0J | OLD-KME1GXQF | NULL |
| 6 | XZV0EY0G | OLD-N06BURDF | NULL |
| 7 | 9HBQZ88V | OLD-76HSPUAL | NULL |
| 8 | YI5AT6G4 | OLD-X8KAWD7Z | NULL |
| 9 | XSR0KA3V | NEW-O7RAR81X | REVISED |
| 10 | MML3HN48 | NEW-B02PFB63 | REVISED |
| 11 | 9HBQZ88V | NEW-76HSPUAL | REVISED |
| 12 | YI5AT6G4 | NEW-X8KAWD7Z | XYZ |
| 13 | Z8H2B5KY | 3RINJV0K | REVISED |
+----+----------+--------------+---------+
We would like to design a query that does not return any tuples that are REVISED by other tuples. In our case the output should look like this:
+----+----------+--------------+---------+
| id | colA | colB | revFlag |
+----+----------+--------------+---------+
| 2 | 4F2JG71O | OLD-E71BE63L | NULL |
| 4 | 5H0MWVSB | OLD-V70XLGHT | NULL |
| 5 | JW73ZX0J | OLD-KME1GXQF | NULL |
| 6 | XZV0EY0G | OLD-N06BURDF | NULL |
| 8 | YI5AT6G4 | OLD-X8KAWD7Z | NULL |
| 9 | XSR0KA3V | NEW-O7RAR81X | REVISED |
| 10 | MML3HN48 | NEW-B02PFB63 | REVISED |
| 11 | 9HBQZ88V | NEW-76HSPUAL | REVISED |
| 12 | YI5AT6G4 | NEW-X8KAWD7Z | XYZ |
| 13 | Z8H2B5KY | 3RINJV0K | REVISED |
+----+----------+--------------+---------+
You can use a NOT EXISTS clause to filter out all rows for which another row exists which has the same colA value and revFlag = 'REVISED':
SELECT *
FROM myTable t1
WHERE NOT EXISTS (
SELECT *
FROM myTable t2
WHERE t2.id != t1.id AND t2.colA = t1.colA AND t2.revFlag = 'REVISED'
)
Output:
id colA colB revFlag
2 4F2JG71O OLD-E71BE63L (null)
4 5H0MWVSB OLD-V70XLGHT (null)
5 JW73ZX0J OLD-KME1GXQF (null)
6 XZV0EY0G OLD-N06BURDF (null)
8 YI5AT6G4 OLD-X8KAWD7Z (null)
9 XSR0KA3V NEW-O7RAR81X REVISED
10 MML3HN48 NEW-B02PFB63 REVISED
11 9HBQZ88V NEW-76HSPUAL REVISED
12 YI5AT6G4 NEW-X8KAWD7Z XYZ
13 Z8H2B5KY 3RINJV0K REVISED
Demo on dbfiddle
SELECT stuff
FROM somewhere x
LEFT
JOIN somewhere y
ON y.thing = x.thing
AND y.otherthing = x.otherthing
AND y.anotherthing > x.anotherthing
AND y.whatever = 'some value'
WHERE y.anotherthing .... ;
You could use the IN Clause
Schema (MySQL v8.0)
CREATE TABLE table1 (
`id` INTEGER,
`colA` VARCHAR(8),
`colB` VARCHAR(12),
`revFlag` VARCHAR(7)
);
INSERT INTO table1
(`id`, `colA`, `colB`, `revFlag`)
VALUES
('1', 'XSR0KA3V', 'OLD-O7RAR81X', NULL),
('2', '4F2JG71O', 'OLD-E71BE63L', NULL),
('3', 'MML3HN48', 'OLD-B02PFB63', NULL),
('4', '5H0MWVSB', 'OLD-V70XLGHT', NULL),
('5', 'JW73ZX0J', 'OLD-KME1GXQF', NULL),
('6', 'XZV0EY0G', 'OLD-N06BURDF', NULL),
('7', '9HBQZ88V', 'OLD-76HSPUAL', NULL),
('8', 'YI5AT6G4', 'OLD-X8KAWD7Z', NULL),
('9', 'XSR0KA3V', 'NEW-O7RAR81X', 'REVISED'),
('18', 'XSR0KA3V', 'NEW-O7RAR81X', 'ZRNTR'),
('10', 'MML3HN48', 'NEW-B02PFB63', 'REVISED'),
('11', '9HBQZ88V', 'NEW-76HSPUAL', 'REVISED'),
('12', 'YI5AT6G4', 'NEW-X8KAWD7Z', 'XYZ'),
('13', 'Z8H2B5KY', '3RINJV0K', 'REVISED');
Query #1
SELECT
`id`, `colA`, `colB`, `revFlag`
FROM
table1 t1
WHERE
(`colA` , IFNULL(`revFlag`,0)) IN
(SELECT
`colA`, `revFlag`
FROM
table1
WHERE
`revFlag` = 'REVISED' UNION SELECT
`colA`, IFNULL(MAX(`revFlag`),0)
FROM
table1
WHERE
`colA` NOT IN (SELECT
`colA`
FROM
table1
WHERE
`revFlag` = 'REVISED')
GROUP BY `colA`)
ORDER BY id;
id
colA
colB
revFlag
2
4F2JG71O
OLD-E71BE63L
4
5H0MWVSB
OLD-V70XLGHT
5
JW73ZX0J
OLD-KME1GXQF
6
XZV0EY0G
OLD-N06BURDF
9
XSR0KA3V
NEW-O7RAR81X
REVISED
10
MML3HN48
NEW-B02PFB63
REVISED
11
9HBQZ88V
NEW-76HSPUAL
REVISED
12
YI5AT6G4
NEW-X8KAWD7Z
XYZ
13
Z8H2B5KY
3RINJV0K
REVISED
View on DB Fiddle

Increment value depending other one

Let's assume I have the table:
id | val_1 | val_2
1 | 1 | 0
2 | 1 | 1
3 | 1 | 2
4 | 2 | 0
val_2 should be zero at first if there was no rows with val_1 before. Otherwise it should be previous val_2 + 1 for this val_1.
I can't figure it out by myself the best way to do it. The one thing I've invented is trigger after insert, but I think here maybe some other way to do it cleaner and faster?
My code is something like:
DELIMITER $$
CREATE TRIGGER after_table_insert
AFTER INSERT
ON table FOR EACH ROW
BEGIN
UPDATE table SET val_2 = t.val_2 + 1
FROM (
SELECT val_2 FROM table WHERE val_1 = new.val_1 ORDER BY id DESC LIMIT 1
) t
WHERE id = new.id;
END$$
DELIMITER ;
I will appreciate for any help!
Have a great day/night.
You have couple of issues with such setup:
What's going on if you UPDATE or DELETE rows? It can mess up everything with val2. Be careful with that.
Val2 can always be calculated and there is no need to store it.
Having said that, below I will show you a setup with which I will store only id and val1. Then val2 will be calculated within the SELECT statement (so it will always be correct).
CREATE TABLE vals(
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
val INT NOT NULL
);
INSERT INTO vals(val) VALUES(1),(1),(1),(2);
Now what I am going to do is to use the ROW_NUMBER() function (which prints the row number) and run it over a PARTITION BY val:
SELECT id, val,
ROW_NUMBER() OVER (
PARTITION BY val
) AS val2
FROM vals;
We are almost there. Sadly it will offset them by 1 compared to what you need:
+----+-----+------+
| id | val | val2 |
+----+-----+------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
+----+-----+------+
The fix is simple. Just add "-1" to it and you are ready.
SELECT id, val,
-1+ROW_NUMBER() OVER (
PARTITION BY val
) AS val2
FROM vals;
This will produce:
+----+-----+------+
| id | val | val2 |
+----+-----+------+
| 1 | 1 | 0 |
| 2 | 1 | 1 |
| 3 | 1 | 2 |
| 4 | 2 | 0 |
+----+-----+------+
With this solution there is no need to store val2 at all (you can create it as a VIEW if you wish) and it is not vulnerable to the issue when you delete a row (it will continue to work properly).
This is one possibility
Schema (MySQL v5.7)
CREATE TABLE table1 (
`id` INTEGER,
`val_1` INTEGER,
`val_2` INTEGER
);
DELIMITER //
CREATE TRIGGER after_table_insert
BEFORE INSERT
ON table1 FOR EACH ROW
BEGIN
SET #maxval2 = 0;
SELECT max(val_2) + 1 into #maxval2 FROM table1 WHERE val_1 = new.val_1;
IF #maxval2 IS NULL THEN
SET #maxval2 = 0;
END IF;
SET NEW.val_2 = #maxval2;
END//
DELIMITER ;
INSERT INTO table1
(`id`, `val_1`, `val_2`)
VALUES
('1', '1', '0'),
('2', '1', '0'),
('3', '1', '0'),
('4', '2', '0'),
('4', '2', '0');
Query #1
SELECT * FROM table1;
| id | val_1 | val_2 |
| --- | ----- | ----- |
| 1 | 1 | 0 |
| 2 | 1 | 1 |
| 3 | 1 | 2 |
| 4 | 2 | 0 |
| 4 | 2 | 1 |
View on DB Fiddle

How to extract different json elements from the same table in SQL query?

I am querying from a table with the following format:
id|provider|score
--------------------------------
1 | att | '{"attscore":300}'
1 | verizon | '{"verizonscore":299}'
2 | att | '{"attscore":200}'
3 | verizon | '{"verizonscore":155}'
I am trying to get a table that looks like the following:
id|attscore|verizonscore
-------------------------
1 | 300 | 299
2 | 200 | null
3 | null | 155
Note that used to json in sql
CREATE TABLE table1 (
`id` INTEGER,
`provider` VARCHAR(7),
`score` VARCHAR(22)
);
INSERT INTO table1
(`id`, `provider`, `score`)
VALUES
('1', 'att', '{"attscore":300}'),
('1', 'verizon', '{"verizonscore":299}'),
('2', 'att', '{"attscore":200}'),
('3', 'verizon', '{"verizonscore":155}');
SELECT
id,
GROUP_CONCAT(CASE WHEN provider = 'att' THEN `score`->"$.attscore" ELSe NULL END) attscore
,GROUP_CONCAT(CASE WHEN provider = 'verizon' THEN `score`->"$.verizonscore" ELSe NULL END) verizonscore
FROM table1
GROUP BY id
id | attscore | verizonscore
-: | :------- | :-----------
1 | 300 | 299
2 | 200 | null
3 | null | 155
db<>fiddle here
This works with a fixed number of column quite well, if you have much more of these you need to do something like this

How to get partial sum of column and finds which rows has partial sum in mysql

<!-- language: lang-none -->
+----------------------------------------+
| Here is my sample table |
+----------------------------------------+
| Date Person Column_1 Column_2 |
| 15-03-13 A 100 NULL |
| 15-03-13 B NULL 100 |
| 16-03-13 A 100 50 |
| 16-03-13 B NULL NULL |
| 17-03-13 A 100 50 |
| 17-03-13 B 20 30 |
+----------------------------------------+
Now i wanted to do sum(column_1) by date. But i also need to which sum of row includes NULL data.
here is the output result I wanted to achieve
+---------------------------------+
| Date SUM includesNullval |
+---------------------------------+
| 15-03-13 100 true |
| 16-03-13 100 true |
| 17-03-13 120 false |
+---------------------------------+
I don't know to achieve above output. Can anybody give me any idea or solution of this problem?
Note both the form and content of the answer.
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(date DATE NOT NULL
,person CHAR(1) NOT NULL
,column_1 INT NULL
,column_2 INT NULL
,PRIMARY KEY(date,person)
);
INSERT INTO my_table VALUES
('2013-03-15','A', 100, NULL),
('2013-03-15','B',NULL, 100),
('2013-03-16','A', 100, 50),
('2013-03-16','B',NULL, NULL),
('2013-03-17','A', 100, 50),
('2013-03-17','B', 20, 30);
SELECT date, SUM(column_1), MAX(column_2 IS NULL) incnull FROM my_table GROUP BY date;
+------------+---------------+---------+
| date | SUM(column_1) | incnull |
+------------+---------------+---------+
| 2013-03-15 | 100 | 1 |
| 2013-03-16 | 100 | 1 |
| 2013-03-17 | 120 | 0 |
+------------+---------------+---------+
Using a case statement will help you here:
Select Date_Column, sum(column_A),
MAX(CASE when column_A is NULL then 'True' else 'False' end) "Includes_NULL_Value"
from table_x
group by Date_column;
REX TESTER

MySQL: select and divide by count using group by

Here's a data set of my table
ID |groupID| Start | End | SegStart | SegEnd | something
-------------------------------------------------------------------
1 | 1 | 0.234 | 0.345 | 0.345 | 0.677 | 0
2 | 1 | 0.234 | 0.345 | 0.346 | 0.678 | 0
3 | 1 | 0.234 | 0.345 | 0.347 | 0.679 | 0
4 | 1 | 0.234 | 0.345 | 0.348 | 0.680 | 1
5 | 2 | 0.345 | 0.567 | 0.568 | 0.570 | 0
6 | 2 | 0.345 | 0.567 | 0.569 | 0.571 | 1
7 | 3 | 0.567 | 0.678 | 0.679 | 0.681 | 0
8 | 3 | 0.567 | 0.678 | 0.680 | 0.682 | 0
9 | 3 | 0.567 | 0.678 | 0.681 | 0.683 | 1
I want to calculate a value from the Start / End columns as well as the SegStart / SegEnd columns where something = "1" and then divide this value by the number of items in a group (group 1 has 4 items, group 2 has 2, group 3 has 3, etc)
I've tried this query, but it gives me the error "Subquery returns more than 1 row"
select (((End - Start) - (SegEnd - SegStart)) /
(select count(*) as NumSeg from table group by groupID)) as NewValue
from table where something = "1";
I would like a list of the new value for each group, kind of like this (values are imaginary):
groupID | NewValue
--------------------
1 | 0.102
2 | 0.110
3 | 0.036
You could try this:
CREATE TABLE mytable(
ID INTEGER NOT NULL PRIMARY KEY
,groupID INTEGER NOT NULL
,Start NUMERIC(11,3) NOT NULL
,End NUMERIC(7,3) NOT NULL
,SegStart NUMERIC(11,3) NOT NULL
,SegEnd NUMERIC(11,3) NOT NULL
,something BIT NOT NULL
);
INSERT INTO mytable(ID,groupID,Start,End,SegStart,SegEnd,something) VALUES (1,1,0.234,0.345,0.345,0.677,0);
INSERT INTO mytable(ID,groupID,Start,End,SegStart,SegEnd,something) VALUES (2,1,0.234,0.345,0.346,0.678,0);
INSERT INTO mytable(ID,groupID,Start,End,SegStart,SegEnd,something) VALUES (3,1,0.234,0.345,0.347,0.679,0);
INSERT INTO mytable(ID,groupID,Start,End,SegStart,SegEnd,something) VALUES (4,1,0.234,0.345,0.348,0.680,1);
INSERT INTO mytable(ID,groupID,Start,End,SegStart,SegEnd,something) VALUES (5,2,0.345,0.567,0.568,0.570,0);
INSERT INTO mytable(ID,groupID,Start,End,SegStart,SegEnd,something) VALUES (6,2,0.345,0.567,0.569,0.571,1);
INSERT INTO mytable(ID,groupID,Start,End,SegStart,SegEnd,something) VALUES (7,3,0.567,0.678,0.679,0.681,0);
INSERT INTO mytable(ID,groupID,Start,End,SegStart,SegEnd,something) VALUES (8,3,0.567,0.678,0.680,0.682,0);
INSERT INTO mytable(ID,groupID,Start,End,SegStart,SegEnd,something) VALUES (9,3,0.567,0.678,0.681,0.683,1);
select A.GROUPID, ((End - Start) - (SegEnd - SegStart)) / B.NumSeg AS V1
from mytable A
INNER JOIN (select GROUPID, count(*) as NumSeg from mytable group by GROUPID) B ON A.GROUPID = B.GROUPID
where something = "1";
Output:
GROUPID V1
1 1 -0,0552500
2 2 0,1100000
3 3 0,0363333