一連の値から、テーブルの列に格納されていない値を見つけるにはどうすればよいですか?


12

数十万の整数を潜在的に格納するテーブルがあります

desc id_key_table;

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

プログラムから、私は整数の大きなセットを持っています。上記のid_key列にない整数を確認したいのですが。

これまでのところ、私は次のアプローチを考え出しました:

1)各整数を反復処理し、以下を実行します。

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

countが0の場合、id_keyはテーブルにありません。

これは恐ろしくて恐ろしい方法のようです。


2)一時テーブルを作成し、各値を一時テーブルに挿入して、2つのテーブルでJOINを実行します。

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;

これは最良のアプローチのようですが、まだ考えていないはるかに優れたアプローチがあると確信しています。一時テーブルを作成する必要がなく、1つのクエリを使用して、不足している整数を特定したいと思います。

このタイプの検索に適切なクエリはありますか?

(MySQL)


2
私はそれはいくつかの並べ替え(ないDBA自体)のプログラムとの相互作用を扱うよう、あなたの質問(DBAへようこそ)しかし、それはおそらくはるかに適切なStackOverflowの上で尋ねたどのように好き
デレク・ダウニー

歓迎してくれてありがとう、私はこのような場所にはスタックオーバーフローよりも多少の教祖がいるのではないかと思いました。私はそこに再度尋ねてもかまいません。
クリントン

2
示唆したように、私はStackOverflowのに転載:stackoverflow.com/questions/5967822/...
クリントン

この質問では、SQLサーバーについても同様の状況が扱われました。大量のデータをストアドプロシージャに送信するための手法。問題は他のデータベース環境でも同様であることがわかります。とにかく、私はソリューションなしに行きます。2-IDのリストを送信し、解析し、テーブルに入れ、メインテーブルに結合します。他のソリューションを使用できない場合は、ここで掘る必要があります:-)。
マリアン

回答:


7

LEFT JOINを使用する2番目のソリューションは、断然最良のアプローチです。一時テーブルは使用せず、通常のテーブルを使用して、クエリを実行するときにいつでも新しい値を入力します。


5

「大きな整数のセット」は、「数十万の整数」の表よりもかなり小さいようです。その仮定では、MySQLでSQLステートメントのテーブルとして整数の配列を使用する方法がない限り、2番目のオプションがおそらく最良です。一時テーブルとメインテーブルのインデックスのフルスキャンを実行する必要があります。主な利点は、数十万の整数を含むインデックスを一度にスキャンするだけで済み、クライアントに結果を送信するだけでよいことです。クエリは次のように書き直すことができます(ただし、そうする必要はありません)。

SELECT * FROM id_key_table_temp 
WHERE id_key NOT IN (select id_key FROM id_key_table);

MySQLプラットフォームの違いを知らないため、通常のテーブルよりも一時テーブルを推奨していません。Oracleではおそらく一時テーブルが最適ですが、Oracleでは配列をテーブルとして使用し、それに直接結合するだけです。
Leigh Riffel、

3

一時テーブルを使用してで挿入する代わりに、チェックしようとしているすべての値を使用してサブクエリ作成insert into id_key_table_temp values (1),(2),(3),...,(500),(501);できます。

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);

2

私のコメントで述べたように、これはおそらくスタックオーバーフローにより適しています。ただし、これらのソリューションはどちらも最適ではないと思います。

ソリューション1は複数の選択呼び出しを必要とし、非常に非効率的です

ソリューション2の方が優れていますが、その多くの値を挿入するコストが最適なソリューションであるかどうかはわかりません。

可能な解決策3は、1つのクエリを作成することです。

SELECT DISTINCT id_key FROM id_key_table

プログラムで整数セットとDBの内容との違いを取得します。最悪の場合、(整数が多いため)このルートはソリューション1よりも優れているはずです。ソリューション2は、多くの整数を返す可能性もあります(データセットにない多数のテーブルがある場合)。依存™!


結果セットが非常に大きくなるため、私はこのソリューションのファンではありません。
クリントン

@Clintonはtrueですが、フィルターで除外するのに十分な整数を提供しない場合、2番目のソリューションでも非常に大きくなる可能性があります。
Derek Downey、

2

StackOverflowでこれにほとんど対処しましたが、永続的な一時(PermTemp)テーブルの使用についてさらに詳しく説明したいと思います。(永続的な温度、それはoxymoronではありませんか?)

StackOverflowの、私は、ストアドプロシージャtest.CreateSampleTableとtest.GetMissingIntegersは、サンプルテーブルを作成していたし、その後の違いを見つけるために、JOIN大きなを行う前に移入するには、ダイナミック一時テーブルを作成します。

今回は、永続テーブルテーブルとともにサンプルテーブルを作成します。

test.LoadSampleTablesは次のとおりです。

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 ;

これを実行した後、テーブルとその内容は次のとおりです。

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)

これは、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 ;

次に、レコードの新しいバッチをインポートします。テーブルtest.weekly_batch、以前に使用されたいくつかのキー、ブランドスパンキング新しいその他のキー:

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

test.weekly_batchを受け取り、それをtest.id_key_table_keysに安全にマージして、テーブルtest.new_keys_to_loadを作成しましょう。

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 ;

結果は次のとおりです。

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

この時点から、インポートするブランドスパンキングの新しいキーのリストとしてnew_keys_to_loadテーブルを使用します。new_keys_to_loadはPermTempテーブルよりも小さいため、LEFT JOINの左側で常にnew_keys_to_loadを使用する必要があります。


私はすでにSOでこれに答えまし
RolandoMySQLDBA
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.