select not in with multiple fields on same table - mysql

I am struggling to build a MySql query to identify missing rows in a table.
The table T structure is the following:
+++++++++++++++++++++++++++++++++++++++++++++++
+ Unique ID + Group + Key1 + Key2 + Value +
+++++++++++++++++++++++++++++++++++++++++++++++
+ 34 + A + d1 + e2 + 123 +
+ 35 + A + d1 + e3 + 456 +
+ 36 + A + d1 + e1 + 444 +
+ 37 + A + d2 + e3 + 555 +
+ 38 + B + d1 + e3 + 555 +
+ 39 + B + d3 + e2 + 111 +
+ ... + ... + ... + ... + ... +
+++++++++++++++++++++++++++++++++++++++++++++++
Rows are grouped with label A and B. I need to identify the set of rows in group A but not in group B by taking Key1 and Key2 into account. Only Unique ID is unique in the table.
In other words, the query should return:
+++++++++++++++++++++++++++++++++++++++++++++++
+ Unique ID + Group + Key1 + Key2 + Value +
+++++++++++++++++++++++++++++++++++++++++++++++
+ 34 + A + d1 + e2 + 123 +
+ 36 + A + d1 + e1 + 444 +
+ 37 + A + d2 + e3 + 555 +
+++++++++++++++++++++++++++++++++++++++++++++++

I would use not exists;
select ta.*
from t ta
where ta.group = 'A' and
not exists (select 1
from t tb
where tb.group = 'B' and tb.key1 = ta.key1 and tb.key2 = ta.key2
);
In MySQL, you can also use multi-column in:
select ta.*
from t ta
where ta.group = 'A' and
(ta.key1, ta.key2) not in (select tb.key1, tb.key2 from t tb where tb.group = 'B');
I prefer not exists simply because many databases don't support multi-column in.

Related

how to using group by with condition in mysql

I have some question about how to using group by room_location_id, but when the column room_code is having different value the value is not grouping, so I have some table like this :
+-------+------------------+-----------+
+ id + room_location_id + room_code +
+-------+------------------+-----------+
+ 1 + 1 + 100 +
+-------+------------------+-----------+
+ 2 + 1 + 100 +
+-------+------------------+-----------+
+ 3 + 1 + 100 +
+-------+------------------+-----------+
+ 4 + 2 + 100 +
+-------+------------------+-----------+
+ 5 + 2 + 100 +
+-------+------------------+-----------+
+ 6 + 2 + 100 +
+-------+------------------+-----------+
+ 7 + 1 + 101 +
+-------+------------------+-----------+
+ 8 + 1 + 101 +
+-------+------------------+-----------+
+ 9 + 1 + 101 +
+-------+------------------+-----------+
and for the condition is when room_location_id have the same value with another row value and when is room_code get the same value too, then the id will grouping, but when room_location_id doesn't have the same value with another row and for the room_code too is not having the same value, then the id will make a new row, for the table result is like this :
+-----------+------------------+-----------+
+ id + room_location_id + room_code +
+-----------+------------------+-----------+
+ 1,2,3 + 1 + 100 +
+-----------+------------------+-----------+
+ 4,5,6 + 2 + 100 +
+-----------+------------------+-----------+
+ 7,8,9 + 1 + 101 +
+-----------+------------------+-----------+
Use GROUP_CONCAT:
SELECT GROUP_CONCAT(id) AS id, room_location_id, room_code
FROM yourTable
GROUP BY room_location_id, room_code
ORDER BY room_code, room_location_id;

Mysql Auto increment value in select grouped by Id

I have 3 MySql tables and I need to use some information to populate a new table.
SELECT table1.products_id as 'ID',
concat(table2.table2_name,':',table2.table2_name,':0') as 'att',
concat(table3.table3_name,':',(#cnt := #cnt + 1)) as 'val'
FROM table1, table2, table3
CROSS JOIN (SELECT #cnt := 0) AS dummy
WHERE table1.options_id=table2.table2_id
AND table1.options_values_id=table3.table3_id
AND table2.language_id=4 AND table3.language_id=4
Using this query I obtain all these (correct) informations
+++++++++++++++++++++++++++++++++++++
+ ID + att + val +
+++++++++++++++++++++++++++++++++++++
+ 22 + Taglia:Taglia:0 + S:1 +
+ 22 + Taglia:Taglia:0 + M:2 +
+ 22 + Taglia:Taglia:0 + L:3 +
+ 55 + Taglia:Taglia:0 + S:4 +
+ 55 + Taglia:Taglia:0 + M:5 +
+ 60 + Taglia:Taglia:0 + 1:6 +
+ 60 + Taglia:Taglia:0 + 2:7 +
+ 60 + Taglia:Taglia:0 + 3:8 +
+ 62 + Taglia:Taglia:0 + 8,5:9 +
+++++++++++++++++++++++++++++++++++++
but I need that the autoincrement value restart when ID change, like this:
+++++++++++++++++++++++++++++++++++++
+ ID + att + val +
+++++++++++++++++++++++++++++++++++++
+ 22 + Taglia:Taglia:0 + S:1 +
+ 22 + Taglia:Taglia:0 + M:2 +
+ 22 + Taglia:Taglia:0 + L:3 +
+ 55 + Taglia:Taglia:0 + S:1 +
+ 55 + Taglia:Taglia:0 + M:2 +
+ 60 + Taglia:Taglia:0 + 1:1 +
+ 60 + Taglia:Taglia:0 + 2:2 +
+ 60 + Taglia:Taglia:0 + 3:3 +
+ 62 + Taglia:Taglia:0 + 8,5:1 +
+++++++++++++++++++++++++++++++++++++
How can I do this ?
Partially solved...
#Shadow comment guide me in the right way, but I'm doing something wrong :(
NEW QUERY
SET #num := 0, #type := '';
SELECT *
FROM ( select table1.products_id as id, concat(table2.table2_name,':',table2.table2_name,':0') as 'attributo',concat(table3.table3_name,':',#num) as 'valore' FROM table1, table2, table3
WHERE table1.options_id=table2.table2_id AND table1.options_values_id=table3.table3_id AND table2.language_id=4 AND table3.language_id=4
ORDER BY `table1`.`products_id` ASC) as table_name2
WHERE 0 <= GREATEST(
#num := IF(#type = id, #num + 1, 1),
LEAST(0, LENGTH(#type := id)))
NEW RESULT
+++++++++++++++++++++++++++++++++++++
+ ID + att + val +
+++++++++++++++++++++++++++++++++++++
+ 22 + Taglia:Taglia:0 + S:2 +
+ 22 + Taglia:Taglia:0 + M:4 +
+ 22 + Taglia:Taglia:0 + L:6 +
+ 55 + Taglia:Taglia:0 + S:2 +
+ 55 + Taglia:Taglia:0 + M:4 +
+ 60 + Taglia:Taglia:0 + 1:2 +
+ 60 + Taglia:Taglia:0 + 2:4 +
+ 60 + Taglia:Taglia:0 + 3:6 +
+ 62 + Taglia:Taglia:0 + 8,5:2 +
+++++++++++++++++++++++++++++++++++++
Where I'm wrong?

Please anyone help about my case in mysql query

I have this data..
+------+--------------+------------+
+ id + position_id + name +
+------+--------------+------------+
+ 1 + 1 + name_1 +
+ 2 + 5 + name_2 +
+ 3 + 2 + name_3 +
+ 4 + 2 + name_4 +
+ 5 + 2 + name_5 +
+ 6 + 3 + name_6 +
+ 7 + 4 + name_7 +
+ 8 + 3 + name_8 +
+ 9 + 2 + name_9 +
+------+--------------+------------+
..then I want the the results is like
+--------------+-----------+----------+----------+-----------+
+ position_id + result1 + result2 + result3 + result4 +
+--------------+-----------+----------+----------+-----------+
+ 1 + name_1 + # + # + # +
+ 2 + name_3 + name_4 + name_5 + name_9 +
+ 3 + name_6 + name_8 + # + # +
+ 4 + name_7 + # + # + # +
+ 5 + name_2 + # + # + # +
+--------------+-----------+----------+----------+-----------+
I have some case for resulting data, this data is for my school reports. The data results must be dynamic following primary of position and if the result is empty will sowing #.
For more data or information you can ask with the following command
If a fixed number of columns then you could do something like this:-
SELECT a.position_id,
COALESCE(MIN(a.name), '#') AS result1,
COALESCE(MIN(b.name), '#') AS result2,
COALESCE(MIN(c.name), '#') AS result3,
COALESCE(MIN(d.name), '#') AS result4
FROM some_table a
LEFT OUTER JOIN some_table b ON a.position_id = b.position_id AND a.id < b.id
LEFT OUTER JOIN some_table c ON a.position_id = c.position_id AND b.id < c.id
LEFT OUTER JOIN some_table d ON a.position_id = d.position_id AND c.id < d.id
GROUP BY a.position_id
With a variable number of columns it isn't going to be possible really without dynamically creating the SQL based on the number of columns, or doing something nasty with GROUP_CONCAT.
But this isn't likely to be efficient.
It would probably be better to do a query to get the first results and then sort out the formatting in the calling script.
EDIT
Time for some nasty code, and i still needs polishing!
First bit is a stored procedure. This gets the max number of columns (gets it slightly wrong, but should be easy to fix with a bit of effort, and works for now) and dynamically builds up the SQL to create a temp table with this number of columns, and then populates it.
DELIMITER ;;
CREATE DEFINER=CURRENT_USER PROCEDURE stored_procedure_name()
BEGIN
DECLARE sql1 TEXT;
DECLARE sql2 TEXT;
DECLARE sql3 TEXT;
SET ##group_concat_max_len = 32000;
SELECT
GROUP_CONCAT(CONCAT('MIN(a', (1 + units.iCnt + 10 * tens.iCnt), '.name) AS result', (1 + units.iCnt + 10 * tens.iCnt)) ORDER BY (1 + units.iCnt + 10 * tens.iCnt)),
GROUP_CONCAT(CONCAT('LEFT OUTER JOIN some_table a', (1 + units.iCnt + 10 * tens.iCnt), ' ON a', (units.iCnt + 10 * tens.iCnt), '.position_id = a', (1 + units.iCnt + 10 * tens.iCnt), '.position_id AND a', (units.iCnt + 10 * tens.iCnt), '.id < a', (1 + units.iCnt + 10 * tens.iCnt), '.id') ORDER BY (1 + units.iCnt + 10 * tens.iCnt) SEPARATOR ' '),
GROUP_CONCAT(CONCAT('result',(1 + units.iCnt + 10 * tens.iCnt), ' VARCHAR(255)') ORDER BY (1 + units.iCnt + 10 * tens.iCnt))
INTO sql1, sql2, sql3
FROM
(
SELECT MAX(count_name) as max_count_name
FROM
(
SELECT COUNT(name) as count_name
FROM some_table
GROUP BY position_id
) sub0
) sub1,
(SELECT 1 iCnt UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) units,
(SELECT 1 iCnt UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 0) tens
WHERE max_count_name >= (units.iCnt + 10 * tens.iCnt);
DROP TEMPORARY TABLE IF EXISTS temp1;
SET #sqlmain1 = CONCAT('CREATE TEMPORARY TABLE temp1(position_id INT, result0 VARCHAR(255), ', sql3, ')');
PREPARE stmt FROM #sqlmain1;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET #sqlmain2 = CONCAT('INSERT INTO temp1 SELECT a0.position_id, MIN(a0.name) AS result0,', sql1, ' FROM some_table a0 ', sql2, ' GROUP BY a0.position_id ');
PREPARE stmt FROM #sqlmain2;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END;;
DELIMITER ;
You can then execute this and then select from the resulting temp table. Note that both statements must be done in the same SQL session, otherwise the temp table will have disappeared by the time you do the select:-
CALL stored_procedure_name();
SELECT * FROM temp1
Hopefully you can pass these both to Jasper.

UPDATE statement, COUNT with multiple WHERE conditions

I have TABLE1 as follows
+----------+----------+----------+----------+----------+
+ date + time + course + runner + position +
+----------+----------+----------+----------+----------+
+ 20120701 + 1200 + london + aaa + 1st +
+ 20120701 + 1200 + london + bbb + 2nd +
+ 20120701 + 1200 + london + ccc + 3rd +
+ 20120701 + 1300 + london + eee + 1st +
+ 20120701 + 1300 + london + fff + 2nd +
+ 20120701 + 1400 + new york + ggg + 1st +
+ 20120701 + 1400 + new york + hhh + 2nd +
+ 20120702 + 2000 + london + iii + 1st +
+ 20120702 + 2000 + london + aaa + 2nd +
+ 20120702 + 2100 + new york + iii + 1st +
+ 20120702 + 2100 + new york + bbb + 2nd +
+----------+----------+----------+----------+----------+
and a second table
+------+----------+------+--------+--------+-----+------+-------+
+idtbl2+ date + time + course + runner + pos + link + total +
+------+----------+------+--------+--------+-----+------+-------+
+ 1 + 20120701 + 1200 + london + aaa + 1st + WWW + +
+ 1 + 20120701 + 1200 + london + aaa + 1st + XXX + +
+ 1 + 20120701 + 1200 + london + aaa + 1st + YYY + +
+ 1 + 20120701 + 1200 + london + aaa + 1st + XXX + +
+------+----------+------+--------+--------+-----+------+-------+
Basically I need to count the number of competitors (RUNNER) in a single event from TABLE1 and update that count into the TOTAL field of the second table, TABLE2.
However, TABLE1 is 8500 records in size, and TABLE2 is 65,000 records in size. When running the following query, it timesout and loses the MySQL connection.
update table2 b, table1 a set b.total = (select count(a.runner) from table1 where a.date = b.date AND a.time = b.time AND a.course = b.course AND a.position != 'DQ' );
My limited understanding of JOINs leads me to believe that a JOIN will not help with the efficiency, so I am quite stuck. Any ideas out there?
#MarkByers -
The JOIN creates a temporary table, a concatenation of both table, yes? My thinking is that no matter what, I still have to compare four fields (date, time, course, runner) from one table against another.
I tried this, but it also times out:
Remove table1 from the update clause, as you are already joining the tables (internally) with the nested Select count.... You are joining table2 and table1 without a where clause, the resulting product is so big to be processed that your engine times out.
Check the where clause in the nested select, I think you should filter the query by Runner, otherwise you are counting different Runners that have the same time, date, course, etc; unless that is what you want.
update table2 b
set b.total = (select count(a.runner)
from table1
where a.date = b.date
AND a.time = b.time
AND a.course = b.course
AND a.position != 'DQ'
AND b.runner = a.runner);

Finding two person id which are there in both column

Below is what I have
+++++++++++++++++
+id1 + id2 +
+++++++++++++++++
+ 1 + 2 +
+ 2 + 1 +
+ 1 + 3 +
+ 3 + 2 +
+ 4 + 5 +
+ 5 + 4 +
+++++++++++++++++
What I want to find is the id of two person which are in both column.
Below is what I will have
+++++++++++++++++
+id1 + id2 +
+++++++++++++++++
+ 1 + 2 +
+ 2 + 1 +
+ 4 + 5 +
+ 5 + 4 +
+++++++++++++++++
OR
+++++++++++++++++
+id1 + id2 +
+++++++++++++++++
+ 1 + 2 +
+ 4 + 5 +
+++++++++++++++++
OR
+++++++++++++++++
+id1 + id2 +
+++++++++++++++++
+ 2 + 1 +
+ 5 + 4 +
+++++++++++++++++
Any idea how to achieve this?
Here is testing data
This should get you started:
SELECT
mytable1.id1, mytable1.id2
FROM
myTable mytable1,
myTable mytable2
WHERE
mytable1.id1 = mytable2.id2
AND
mytable2.id1 = mytable1.id2
ORDER BY
mytable1.id1
Demo
The basic id list is this:
select distinct t1.id1
from mytable t1
join mytable t2 on t1.id1 = t2.id2;
If you want rows:
select distinct t1.*
from mytable t1
join mytable t2 on t1.id1 = t2.id2;
If you want any 2 rows:
select distinct t1.*
from mytable t1
join mytable t2 on t1.id1 = t2.id2
limit 2;