it-swarm-fr.com

À partir d'un ensemble de valeurs, comment trouver les valeurs non stockées dans une colonne de table?

J'ai une table qui potentiellement stocker des centaines de milliers d'entiers

desc id_key_table;

+----------------+--------------+------+-----+---------+-------+
| Field          | Type         | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+-------+
| id_key         | int(16)      | NO   | PRI | NULL    |       |
+----------------+--------------+------+-----+---------+-------+

D'un programme, j'ai un grand ensemble d'entiers. J'aimerais voir lequel de ces entiers ne sont pas dans la colonne ID_Key ci-dessus.

Jusqu'à présent, j'ai proposé les approches suivantes:

1) itérale à travers chaque entier et effectuez un:

select count(*) count from id_key_table where id_key = :id_key

Lorsque le nombre est 0, ID_KEY est manquant dans la table.

Cela semble être un moyen horrible et horrible de le faire.


2) Créez une table temporaire, insérez chacune des valeurs dans la table temporaire et effectuez une jointure sur les deux tables.

create temporary table id_key_table_temp (id_key int(16) primary key );

insert into id_key_table_temp values (1),(2),(3),...,(500),(501);

select temp.id_key
from id_key_table_temp temp left join id_key_table as main 
         on temp.id_key = main.id_key 
where main.killID is null;

drop table id_key_table_temp;

Cela semble être la meilleure approche, cependant, je suis sûr qu'il y a une excellente approche que je n'ai pas encore pensée. Je préférerais ne pas avoir à créer une table temporaire et à utiliser une requête pour déterminer quels entiers manquent.

Y a-t-il une requête appropriée pour ce type de recherche?

(MySQL)

12
Clinton

Votre deuxième solution utilisant la jointure gauche est de loin la meilleure approche. Je n'utiliserais pas de table temporaire, j'utiliserais une table régulière et je le peuplerais avec de nouvelles valeurs à tout moment que vous vouliez exécuter la requête.

Au lieu d'une table temporaire et d'insertion avec insert into id_key_table_temp values (1),(2),(3),...,(500),(501);, vous pouvez construire une sous-requête avec toutes les valeurs que vous essayez de vérifier:

select id_key
from ( select @row := @row + 1 as id_key 
       from (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s1,
            (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s2,
            (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s3,
            (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) s4,
            (select @row:=0) s5 ) s
where id_key in(1, 2, 3, 500, 501)
      and id_key not in (select id_key from main);

Comme indiqué dans mon commentaire, cela est probablement plus adapté à Stackoverflow. Cependant, je pense que ces deux solutions ne sont pas les meilleures:

La solution 1 nécessite plusieurs appels sélectionnés, très inefficace

La solution 2 est meilleure, mais je ne suis pas sûr que le coût de l'insertion de nombreuses valeurs est la meilleure solution.

Une solution possible 3 serait une requête:

SELECT DISTINCT id_key FROM id_key_table

et obtenez la différence de manière programmatique à partir de votre ensemble entier et de ce qui se trouve dans la DB. Au pire, (puisque c'est beaucoup d'entiers), cette route devrait être meilleure que la solution 1. La solution 2 a le potentiel de retourner beaucoup d'entiers (si la table a un groupe qui ne figure pas dans votre ensemble de données), donc dépends ™!

2
Derek Downey

Je suis à peu près abordé cela dans Stackoverflow , mais je voudrais élaborer plus d'informations sur l'utilisation de la table permanente Temp (Permantemple). ( Temp permanent, n'est-ce pas un oxymoron ?)

Dans Stackoverflow , j'avais le test de procédure stocké.CreatsAsampletable et test.getmissingintegers fait une table d'échantillonnage, puis créez une table TEMP dynamique pour renseigner avant de faire la bonne jointure pour trouver des différences.

Cette fois, créons la table des échantillons avec la table de table permanente.

Voici Test.LoadSamplestables:

DELIMITER $$

DROP PROCEDURE IF EXISTS `LoadSampleTables` $$
CREATE DEFINER=`lwdba`@`127.0.0.1` PROCEDURE `LoadSampleTables`(maxinttoload INT)
BEGIN

  DECLARE X,OKTOUSE,MAXLOOP INT;

  DROP TABLE IF EXISTS test.id_key_table;
  DROP TABLE IF EXISTS test.id_key_table_keys;
  CREATE TABLE test.id_key_table (id_key INT(16)) ENGINE=MyISAM;
  CREATE TABLE test.id_key_table_keys (id_key INT(16)) ENGINE=MyISAM;

  SET X=1;
  WHILE X <= maxinttoload DO
    INSERT INTO test.id_key_table VALUES (X);
    SET X = X + 1;
  END WHILE;
  ALTER TABLE test.id_key_table ADD PRIMARY KEY (id_key);

  SET MAXLOOP = FLOOR(SQRT(maxinttoload));
  SET X = 2;
  WHILE X <= MAXLOOP DO
    DELETE FROM test.id_key_table WHERE MOD(id_key,X) = 0 AND id_key > X;
    SELECT MIN(id_key) INTO OKTOUSE FROM test.id_key_table WHERE id_key > X;
    SET X = OKTOUSE;
  END WHILE;
  OPTIMIZE TABLE test.id_key_table;

  INSERT INTO test.id_key_table_keys SELECT id_key FROM test.id_key_table;
  ALTER TABLE test.id_key_table_keys ADD PRIMARY KEY (id_key);
  OPTIMIZE TABLE test.id_key_table_keys;

END $$

DELIMITER ;

Après avoir exécuté cela, voici les tables et leur contenu:

mysql> call test.loadsampletables(25);
+-------------------+----------+----------+----------+
| Table             | Op       | Msg_type | Msg_text |
+-------------------+----------+----------+----------+
| test.id_key_table | optimize | status   | OK       |
+-------------------+----------+----------+----------+
1 row in set (0.20 sec)

+------------------------+----------+----------+----------+
| Table                  | Op       | Msg_type | Msg_text |
+------------------------+----------+----------+----------+
| test.id_key_table_keys | optimize | status   | OK       |
+------------------------+----------+----------+----------+
1 row in set (0.28 sec)

Query OK, 0 rows affected (0.29 sec)

mysql> select * from test.id_key_table;
+--------+
| id_key |
+--------+
|      1 |
|      2 |
|      3 |
|      5 |
|      7 |
|     11 |
|     13 |
|     17 |
|     19 |
|     23 |
+--------+
10 rows in set (0.00 sec)

mysql> select * from test.id_key_table_keys;
+--------+
| id_key |
+--------+
|      1 |
|      2 |
|      3 |
|      5 |
|      7 |
|     11 |
|     13 |
|     17 |
|     19 |
|     23 |
+--------+
10 rows in set (0.00 sec)

Voici les déclencheurs pour la table permtemp

mysql> DELIMITER $$
mysql>
mysql> CREATE TRIGGER test.AddPermTempKey AFTER INSERT ON test.id_key_table
    -> FOR EACH ROW
    -> BEGIN
    ->     INSERT IGNORE INTO test.id_key_table_keys VALUES (NEW.id_key);
    -> END $$
Query OK, 0 rows affected (0.09 sec)

mysql>
mysql> CREATE TRIGGER test.DeletePermTempKey AFTER DELETE ON test.id_key_table
    -> FOR EACH ROW
    -> BEGIN
    ->     DELETE FROM test.id_key_table_keys WHERE id_key = OLD.id_key;
    -> END $$
Query OK, 0 rows affected (0.08 sec)

mysql>
mysql> DELIMITER ;

Maintenant, permettent d'importer un nouveau lot d'enregistrements, TABLE TEST.Weekly_Batch, des clés utilisées auparavant, d'autres clés marquées de la fessée nouveau:

mysql> CREATE TABLE test.weekly_batch (id_key INT(16)) ENGINE=MyISAM;
Query OK, 0 rows affected (0.04 sec)

mysql> INSERT INTO test.weekly_batch VALUES (17),(19),(23),(29),(31),(37),(41);
Query OK, 7 rows affected (0.00 sec)
Records: 7  Duplicates: 0  Warnings: 0

mysql> ALTER TABLE test.weekly_batch ADD PRIMARY KEY (id_key);
Query OK, 7 rows affected (0.08 sec)
Records: 7  Duplicates: 0  Warnings: 0

Prenons le test.weekly_batch et le fusionner en toute sécurité dans Test.Id_Key_Table_Keys et formez le test de table.New_Keys_To_ToLoad:

DELIMITER $$

DROP PROCEDURE IF EXISTS `test`.`ImportWeeklyBatch` $$
CREATE PROCEDURE `test`.`ImportWeeklyBatch` ()
TheStoredProcedure:BEGIN

  DECLARE RCOUNT INT;

  SELECT COUNT(1) INTO RCOUNT FROM information_schema.tables
  WHERE table_schema='test' AND table_name='weekly_batch';
  IF RCOUNT = 0 THEN
    LEAVE TheStoredProcedure;
  END IF;
  SELECT COUNT(1) INTO RCOUNT FROM test.weekly_batch;
  IF RCOUNT = 0 THEN
    LEAVE TheStoredProcedure;
  END IF;
  DROP TABLE IF EXISTS test.new_keys_to_load;
  CREATE TABLE test.new_keys_to_load (id_key INT(16));
  INSERT INTO test.new_keys_to_load (id_key)
  SELECT id_key FROM test.weekly_batch A
  LEFT JOIN test.id_key_table_keys B USING (id_key)
  WHERE B.id_key IS NULL;

  SELECT * FROM test.new_keys_to_load;

END $$

DELIMITER ;

Voici le résultat:

mysql> call test.importweeklybatch;
+--------+
| id_key |
+--------+
|     29 |
|     31 |
|     37 |
|     41 |
+--------+
4 rows in set (0.14 sec)

À partir de ce point, utilisez simplement la table new_keys_to_load en tant que liste de la marque de nouvelles clés à importer. Étant donné que new_keys_to_load est plus petit que la table permtemp, vous devez toujours utiliser new_keys_to_load sur le côté gauche de la jointure gauche.

2
RolandoMySQLDBA