Mysql result dataset filter issue - mysql

Is there a way to filter result as follows:
Dataset:
ID NAME VALUE
-----------------------------------------
23 Test TRUE
24 Test FALSE
25 Test FALSE
26 Test TRUE
27 Test FALSE
28 Test FALSE
Result should be:
ID NAME VALUE
-----------------------------------------
23 Test TRUE
24 Test FALSE
26 Test TRUE
27 Test FALSE
The idea is to return all rows that has value TRUE and first row that has value of false after true

I guess this could be done with a self join, something like:
SELECT ID, NAME, VALUE
FROM table
WHERE VALUE = "TRUE"
UNION ALL
SELECT t2.ID, t2.NAME, t2.VALUE
FROM table t1
JOIN table t2
ON t1.ID +1 = t2.ID
AND t1.VALUE = "TRUE"
AND t2.VALUE = "FALSE"
EDIT: this also only works for nonebreaking IDs, so not usable for OPs case

E.g.:
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,name VARCHAR(12) NOT NULL
,value TINYINT NOT NULL
);
INSERT INTO my_table VALUES
(23,'Test',TRUE),
(24,'Test',FALSE),
(25,'Test',FALSE),
(26,'Test',TRUE),
(27,'Test',FALSE),
(28,'Test',FALSE);
SELECT a.*
FROM my_table a
JOIN
( SELECT x.*
, MIN(y.id) minid
FROM my_table x
JOIN my_table y
ON y.id > x.id
AND y.value = FALSE
WHERE x.value = TRUE
GROUP
BY x.id
) b
ON a.id IN (b.id,b.minid);
+----+------+-------+
| id | name | value |
+----+------+-------+
| 23 | Test | 1 |
| 24 | Test | 0 |
| 26 | Test | 1 |
| 27 | Test | 0 |
+----+------+-------+

Probably only with joining table to same table again, only problem with this sql is that it needs to have non-breaking ID
Data
id name value
1 TEST 1
2 TEST 0
3 TEST 0
4 TEST 0
5 TEST 1
6 TEST 1
7 TEST 0
8 TEST 0
9 TEST 1
Join same table based on id from next row and then all rows from table where value true/1 or current row is t1.value=0 and previous t2.value = 1
SELECT t1.* FROM table as t1
LEFT JOIN table t2 ON t1.id = t2.id + 1
WHERE ( ( t1.value = 1 ) OR ( t1.value = 0 AND t2.value = 1 ) )
ORDER BY t1.id
Result
id name value
1 TEST 1
2 TEST 0
5 TEST 1
6 TEST 1
7 TEST 0
9 TEST 1
EDIT: My last chance, though not sure performance but noncontinous id are working with this
SELECT * FROM my_table as t1
LEFT JOIN my_table t2 ON
t2.id = (
SELECT MAX(my_table.id) FROM my_table
WHERE my_table.id < t1.id ORDER BY id asc LIMIT 1
)
WHERE (t1.value=1) OR (t1.value=0 AND t2.value=1) order by t1.id asc

Related

MySQL - Select results with specified ID or with null

I have one table:
| ID | ADV_ID | USER_ID |
| 1 | 22 | NULL |
| 2 | 22 | 3 |
| 5 | 44 | NULL |
and now, I want to select row where adv_id = 22 and user_id = 3. If that row doesn't exist, I want to get row where adv_id = 22 and user_id is null.
I tried in that way:
SELECT * FROM `table` WHERE adv_id = 22 AND (user_id = 3 OR user_id is null)
but this query return two rows - with user_id = NULL and with user_id = 3. I want to get one row - with user_id = 3 or (if not exist), with user_id = NULL.
How I can do it in one query?
Thanks.
Use conditional aggregation:
SELECT t1.*
FROM yourTable t1
INNER JOIN
(
SELECT
ADV_ID,
CASE WHEN COUNT(CASE WHEN USER_ID = 3 THEN 1 END) > 0 THEN 3 END USER_ID
FROM yourTable
) t2
ON t1.ADV_ID = t2.ADV_ID AND
((t1.USER_ID IS NULL AND t2.USER_ID IS NULL) OR (t1.USER_ID = t2.USER_ID))
WHERE
t1.ADV_ID = 22;
Demo
For an explanation, the subquery I have aliased as t2 aggregates over the ADV_ID, and outputs the value 3 if that value occurs in one or more records, otherwise it outputs NULL. Then, we join this subquery back to your original table on the condition that both USER_ID values are NULL, or, if not, that the two USER_ID values match.
You may modify the demo to see that it generates the output you want for other inputs.
SELECT *
FROM test
WHERE ADV_ID IS NOT NULL AND USER_ID IS NOT NULL
UNION ALL
SELECT *
FROM test
WHERE USER_ID IS NULL AND NOT EXISTS (
SELECT 1
FROM test
WHERE ADV_ID IS NOT NULL AND USER_ID IS NOT NULL
)
Select all rows with the first condition: ADV_ID IS NOT NULL AND USER_ID IS NOT NULL
and then UNION ALL with the same table if the first condition is NOT EXISTS.
So we only get results if the first condition is not returned any rows.
The MySQL UNION ALL operator is used to combine the result sets of 2 or more SELECT statements.
try like that:
SELECT * FROM `table` t1 WHERE (t1.adv_id = 44)
AND ((t1.user_id = 3) OR
(NOT EXISTS (select * from `table` t2 where t2.adv_id=t1.adv_id and t2.user_id = 3) AND t1.user_id is null ))
DEMO

mysql add duplicated field id to a current field

i have table like :
id IP Subnet Duplicates Valid
1 foo 16 1
2 bar 24 1
3 foo 28 1
4 foo 32 1
i want update description with id of duplicated row . something like:
id IP Subnet Duplicates Valid
1 foo 16 3,4 0
2 bar 24 1
3 foo 28 1,4 0
4 foo 32 1,3 0
Here is my query :
update tblSample inner join (
select
t1.Id,
group_concat(t2.Id) dups
from
tblSample t1 inner join tblSample t2
on t1.Id<>t2.Id ) AND
((t1.IP >> (32-LEAST(t1.Subnet,t2.Subnet))
<< (32-LEAST(t1.Subnet,t2.Subnet))
=
((t2.IP >> (32-LEAST(t1.Subnet,t2.Subnet))
<< 32-LEAST(t1.Subnet,t2.Subnet)))
group by
t1.Id
) s on tblSample.Id = s.Id
set
Valid=0 ,Duplicates=dups
my code works but its very slow ( about 53 second for 10000 record )
How can i improve speed ?
is there any way that i can decrease comparison operation.
Here is a solution without self join in your sub query, maybe will not improve performance greatly, but try it, and also, try to explain it and yours.
update tblSample t1
join (
select name, group_concat(id order by id) as description
from tblSample
group by name
) t2
on t1.name = t2.name and cast(t1.id as char) <> t2.description
set t1.description = replace(
replace(
replace(
t2.description,
concat(',', t1.id, ','),
',')
, concat(',', t1.id)
, '')
, concat(t1.id, ',')
, '')
;
Demo Here
you can also use this query for test:
UPDATE dupli d
SET description = (
SELECT CONCAT('duplicate in ',GROUP_CONCAT(`id` ORDER BY id))
FROM (SELECT * FROM dupli) AS d1
WHERE `name` = d.`name` AND id <> d.id ) ;
sample
MariaDB [yourSchema]> UPDATE dupli d
-> SET description = (
-> SELECT CONCAT('duplicate in ',GROUP_CONCAT(`id` ORDER BY id))
-> FROM (SELECT * FROM dupli) AS d1
-> WHERE `name` = d.`name` AND id <> d.id ) ;
Query OK, 0 rows affected, 1 warning (0.00 sec)
Rows matched: 4 Changed: 0 Warnings: 1
MariaDB [yourSchema]> select * from dupli;
+----+------+------------------+
| id | name | description |
+----+------+------------------+
| 1 | foo | duplicate in 3,4 |
| 2 | bar | NULL |
| 3 | foo | duplicate in 1,4 |
| 4 | foo | duplicate in 1,3 |
+----+------+------------------+
4 rows in set (0.00 sec)
MariaDB [yourSchema]>

MySQL merge two tables with different data

The following are my mysql tables
Table 1:
ID | commonID | Date | text | active
1 11 01.02 abc 1
2 11 02.02 123 1
3 11 03.02 xyz 0
Table 2:
ID | commonID | Date | value | active
1 11 01.02 abc 1
2 11 04.02 123 1
3 11 03.02 xyz 1
The Final result should display this:
| date | text | value
01.02 abc abc
02.02 123 (null)
03.02 (null) xyz
04.02 (null) 123
The Idea here is, to merge the two tables. All entries with a defined commonID like 11 in the example will be selected from both tables. then the tables will be united.
Conditions:
If there are matching dates in TABLE1 and TABLE2 they will be merged
If there is a solo date in TABLE1 or TABLE2, the value/text for the table with no date will become NULL
If there is a record in TABLE1 or TABLE2 that has active = FALSE, it will not be processed.
There can be matching and not matching dates in BOTH tables.
I want to use this for display chronologic events, if there is an event in both tables, there should be only one line for this.
What could be the Solution here?
Try this:
SELECT T1.date,
CASE WHEN T1.active = 1 THEN T1.text END as text,
CASE WHEN T2.active =1 THEN T2.value END as value
FROM Table1 T1 LEFT JOIN
Table2 T2 ON T1.date=T2.date
UNION
SELECT T2.date,
CASE WHEN T1.active = 1 THEN T1.text END as test,
CASE WHEN T2.active = 1 THEN T2.value END as value
FROM Table1 T1 RIGHT JOIN
Table2 T2 ON T1.date=T2.date
Result:
DATE TEXT VALUE
01.02 abc abc
02.02 123 (null)
03.02 (null) xyz
04.02 (null) 123
Sample SQL Fiddle.
Try this:
SELECT t1.date,t1.text,t2.value FROM table1 t1
LEFT JOIN table2 t2 ON t1.commonId = t2.commonId and t1.date = t2.date and t2.active = 1
where t1.active = 1
UNION
SELECT t2.date,t1.text,t2.value FROM table2 t2
LEFT JOIN table1 t1 ON t1.commonId = t2.commonId and t1.date = t2.date and t1.active = 1
where t2.active = 1
Sample http://sqlfiddle.com/#!2/d2c4d/2
Here's one way...
SELECT commonid
, date
, MAX(text) text
, MAX(value) value
FROM
( SELECT id
, commonid
, date
, text
, NULL value
, active
FROM table1
WHERE active <> 0
UNION
SELECT id
, commonid
, date
, NULL
, value
, active
FROM table2
WHERE active <> 0
) x
GROUP
BY commonid,date;
You can move the WHERE active <> 0 bit out of the UNIONs if you like, to just before the GROUP BY.

Complex query over two tables

I have two tables:
table1:
id
--
1
2
3
table2: (left out primary index)
id2 | cond
----------
3 | 1
3 | 0
2 | 1
2 | 1
2 | 0
I would need to construct a query that implicitly calculates this intermediary table:
temp:
id | c1 | c2
------------
1 | 0 | 2
2 | 2 | 2
3 | 1 | 2
with c1 = countRows(id2 == id && cone == 1) and c2 == countRows(id2 = 2 && cond == 1).
and then selects SELECT id FROM temp ORDER BY ABS(c1 - c2)*RAND().
My current try is kind of like:
SELECT id, COUNT(t1.id2) AS c1, COUNT(t2.id2) AS c2
FROM table1 LEFT JOIN (table2 AS t1) ON id=t1.id2 LEFT JOIN (table2 AS t2) ON t2.id2=2
WHERE t1.cond=1 AND t2.cond=1
GROUP BY t1.id2
ORDER BY ABS(c1 - c2)*RAND()
LIMIT 1
Which has multiple problems:
It doesn't select rows with no entry in table2
It doesn't correctly count
There seems to be a problem with group columns (c1, c2) in the ORDER BY part 3.
Help would be appreciated.
Update:
table1 represents players for example
table2 would be rounds played, with cond indicating a win
c1 represents the rounds won by each player
c2 represents the rounds won by a reference player (player 2 in this case)
SELECT t1.id, SUM(IFNULL(t2.cond, 0) = 1) AS c1, (SELECT SUM(cond = 1) FROM table2 WHERE id2 = 2) AS c2
FROM table1 t1
LEFT JOIN table2 t2 ON t1.id = t2.id2
GROUP BY t1.id
ORDER BY ABS(SUM(IFNULL(t2.cond, 0) = 1) - c2) * RAND();
Let's see if the first part of your query is returning valid results..
SELECT
t1.id AS id
COUNT(t2.id2) AS c1
COUNT(CASE WHEN t3.id2 = 2 THEN t3.id2 ELSE NULL END) as c2
FROM
table1 AS t1
LEFT JOIN table2 as t2
ON t1.id = t2.id2 AND t2.cond = 1
LEFT JOIN table2 as t3
ON t3.id2 = 2 AND t3.cond = 1
GROUP BY
t2.id2,
t3.id2
Can you provide schema?

select when record change value

I'm trying to select just records which changed values compared to previous record,
my table is looking like this
Id(int) | value(boolean) |
-------------------------
1 | 0 |
-------------------------
2 | 1 |
-------------------------
3 | 1 |
-------------------------
4 | 1 |
-------------------------
5 | 0 |
-------------------------
6 | 0 |
-------------------------
I must get id:2,5
thanks for your help
I did a self-join, but instead of joining identical ids, I join t1.id = t2.id-1 and then compare the values:
select t2.id
from thetable t1
inner join thetable t2 on t1.id = t2.id-1
where t1.value != t2.value
sqlfiddle: http://sqlfiddle.com/#!2/6d626/4
Edit to add:
Thanks to #Ravinder, I figured out a way to do this even if the ids aren't sequential.
I used this related question.
SET #a :=0;
SET #b :=1;
SELECT t1.id, t1.rownum
FROM
(SELECT if(#a, #a:=#a+1, #a:=1) as rownum, id, value FROM thetable) AS t1
LEFT JOIN
(SELECT if(#b, #b:=#b+1, #b:=1) as rownum, id, value FROM thetable) AS t2
ON t1.rownum = t2.rownum
where t2.value != t1.value
SQLFiddle with non-sequential ids
Basically if you don't have sequential ids you create your own. I called them rownum.
You can use mysql variable in query to check if its new or unchanged
SELECT
`Id`,
`value`,
(CASE when #test=value then 'unchanged'
else 'changed' end) occurance,
#test:=`value` testvar
FROM
Table1
,(SELECT #test:='new') t
HAVING occurance='changed'
ORDER BY `Id`
See fiddle demo