Bir değer kümesinden, tablonun sütununda depolanmayan değerleri nasıl bulabilirim?


12

Yüz binlerce tamsayı depolayacak bir masam var

desc id_key_table;

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

Bir programdan, çok sayıda tamsayı var. Yukarıdaki id_key sütununda bu tamsayılardan hangisinin DEĞİL olduğunu görmek istiyorum.

Şimdiye kadar aşağıdaki yaklaşımları buldum:

1) Her tamsayı üzerinden yineleyin ve şunları yapın:

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

Sayım 0 olduğunda id_key tablodan eksik.

Bu, bunu yapmak için korkunç, korkunç bir yol gibi görünüyor.


2) Geçici bir tablo oluşturun, değerlerin her birini geçici tabloya ekleyin ve iki tabloda bir JOIN gerçekleştirin.

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;

Bu en iyi yaklaşım gibi görünüyor, ancak eminim henüz düşünmediğim çok daha iyi bir yaklaşım var. Geçici bir tablo oluşturmak ve hangi tamsayıların eksik olduğunu belirlemek için bir sorgu kullanmak zorunda kalmayı tercih etmem.

Bu tür bir arama için uygun bir sorgu var mı?

(MySQL)


2
Sorunuzu nasıl sorduğunuzu beğendim (DBA'ya hoş geldiniz), ancak bir çeşit programla (dba per se değil) etkileşimde bulunulması nedeniyle stackoverflow'ta muhtemelen çok daha uygundur
Derek Downey

Karşılama için teşekkür ederim, böyle bir yerin yığın akışından daha fazla guruya sahip olabileceğini düşündüm. Yine de orada tekrar sormak sakıncası yok.
Clinton

2
Önerildiği gibi, StackOverflow'a yanıt verdim: stackoverflow.com/questions/5967822/…
Clinton

Benzer soru bu soruda sql sunucusu için ele alındı: Depolanan proc'a çok fazla veri gönderme tekniği . Orada sorunun diğer db ortamlarında benzer olduğunu bulmalısınız. Her neyse, çözüm no. 2 - kimlik listesi gönderin, ayrıştırın, masaya koyun, ana masanıza katılın. Eğer başka çözümler kullanamazsanız, ama burada :-) kazmak zorunda.
Marian

Yanıtlar:


7

LEFT JOIN kullanarak ikinci çözüm açık ara en iyi yaklaşımdır. Geçici bir tablo kullanmazdım, normal bir tablo kullanır ve sorguyu çalıştırmak istediğinizde yeni değerlerle doldururdum.


5

"Büyük tamsayılar kümesi" hala "yüz binlerce tamsayı" içeren tablodan oldukça küçük gibi görünüyor. Bu varsayımla ve MySQL'de tamsayılarınızın bir dizisini SQL ifadenizde bir tablo olarak kullanmanın bir yolu yoksa, ikinci seçeneğiniz muhtemelen en iyisidir. Geçici tablonun ve ana tablodaki dizinin tam taramasını yapmalıdır. Birincil faydası, sadece yüz binlerce tamsayı içeren dizini bir kez taraması ve yalnızca istemciye sonuçları göndermesi gerektiğidir. Sorgunuz aşağıdaki gibi yeniden yazılabilir (ancak gerekmemektedir):

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

MySQL platformundaki farklılıklar hakkında hiçbir bilgiye sahip olmadığım için normal bir tablo üzerinde geçici bir tabloyu onaylamıyorum. Oracle'da geçici bir tablo muhtemelen en iyisidir, ancak Oracle'da bir diziyi tablo olarak kullanır ve doğrudan ona katılırsınız.
Leigh Riffel

3

Yerine ile takmadan geçici tablonun ve insert into id_key_table_temp values (1),(2),(3),...,(500),(501);, sen verebilir bir alt sorgu inşa çek çalıştığınız tüm değerlerle:

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

Yorumumda belirtildiği gibi, bu muhtemelen yığın akışı için daha uygundur. Ancak, bu çözümlerin her ikisinin de en iyi olmadığını düşünüyorum:

Çözüm 1, birden fazla seçimli çağrı gerektiriyor, çok verimsiz

Çözüm 2 daha iyidir, ancak birçok değeri eklemenin maliyetinin en iyi çözüm olduğundan emin değilim.

Olası bir çözüm 3 bir sorgu yapmak olacaktır:

SELECT DISTINCT id_key FROM id_key_table

ve programlı olarak tamsayı kümenizden ve DB'de bulunanlardan farkı alın. En kötüsü, (çok sayıda tamsayı olduğu için) Bu rota Çözüm 1'den daha iyi olmalıdır. Çözüm 2 AYRICA çok fazla sayıda tamsayı döndürme potansiyeline sahiptir (tablonun veri kümenizde olmayan bir demet varsa), depends ™!


Sonuçların çok büyük olacağı için bu çözümün hayranı değilim.
Clinton

@Clinton true, ancak filtrelemek için yeterli tamsayı sağlamazsanız ikinci çözümünüzde de çok büyük olabilir.
Derek Downey

2

Hemen hemen StackOverflow bu ele , ama daha kalıcı geçici (PermTemp) tablo kullanma hakkında ayrıntılı istiyorum. ( kalıcı sıcaklık, bu bir oksimoron değil mi?)

In StackOverflow , ben saklı yordam test.CreateSampleTable ve test.GetMissingIntegers örnek bir tablo yapmak ve sonra farklılıkları bulmak için JOIN Big yapmadan önce populate dinamik bir geçici tablo oluşturmak vardı.

Bu sefer, kalıcı tablo tablosu ile birlikte örnek tablo oluşturalım.

İşte 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 ;

Bunu çalıştırdıktan sonra, tablolar ve içerikleri şunlardır:

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)

İşte PermTemp tablosunun tetikleyicileri

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 ;

Şimdi, yeni bir toplu kayıt, masa testi.weekly_batch, daha önce kullanılan bazı anahtarlar, diğer anahtarlar marka spanking yeni ithal edelim:

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'ı alalım ve test.id_key_table_keys ile güvenli bir şekilde birleştirelim ve test.new_keys_to_load tablosunu oluşturalım:

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 ;

İşte sonuç:

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

Bu noktadan sonra, içe aktarmak için yeni spanking anahtarlarının listesi olarak new_keys_to_load tablosunu kullanın. New_keys_to_load, PermTemp tablosundan daha küçük olduğu için, LEFT JOIN'in sol tarafında her zaman new_keys_to_load kullanmalısınız.


Bunu SO üzerinde zaten cevapladım
RolandoMySQLDBA
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.