Từ một tập hợp các giá trị, làm cách nào để tìm các giá trị không được lưu trong cột của bảng?


12

Tôi có một bảng có khả năng lưu trữ hàng trăm ngàn số nguyên

desc id_key_table;

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

Từ một chương trình, tôi có một bộ số nguyên lớn. Tôi muốn xem số nguyên nào trong số này KHÔNG có trong cột id_key ở trên.

Cho đến nay tôi đã đưa ra các phương pháp sau:

1) Lặp lại qua từng số nguyên và thực hiện a:

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

Khi đếm bằng 0, id_key bị thiếu khỏi bảng.

Đây dường như là một cách khủng khiếp, khủng khiếp để làm điều đó.


2) Tạo một bảng tạm thời, chèn từng giá trị vào bảng tạm thời và thực hiện THAM GIA trên hai bảng.

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;

Đây có vẻ là cách tiếp cận tốt nhất, tuy nhiên, tôi chắc chắn có một cách tiếp cận tốt hơn nhiều mà tôi chưa nghĩ đến. Tôi không muốn phải tạo một bảng tạm thời và sử dụng một truy vấn để xác định số nguyên nào bị thiếu.

Có một truy vấn thích hợp cho loại tìm kiếm này?

(MySQL)


2
Tôi thích cách bạn hỏi câu hỏi của bạn (Chào mừng bạn đến với DBA), tuy nhiên, nó có lẽ phù hợp hơn nhiều với stackoverflow vì nó liên quan đến việc tương tác với một chương trình nào đó (không phải dba per se)
Derek Downey

Cảm ơn bạn đã chào đón, tôi nghĩ rằng một nơi như thế này có thể có nhiều bậc thầy hơn là stackoverflow. Tôi không ngại hỏi lại mặc dù.
Clinton

2
Theo đề xuất, tôi đã đăng lại lên StackOverflow: stackoverflow.com/questions/5967822/ mẹo
Clinton

Tình huống tương tự đã được xử lý cho máy chủ sql trong câu hỏi này: Kỹ thuật gửi nhiều dữ liệu vào kho lưu trữ . Bạn sẽ thấy rằng vấn đề tương tự trong các môi trường db khác. Dù sao, tôi đi cho giải pháp không. 2 - gửi danh sách id, phân tích cú pháp, đặt vào bảng, tham gia vào bảng chính của bạn. Rằng nếu bạn không thể sử dụng các giải pháp khác, nhưng ở đây bạn phải đào :-).
Mary

Câu trả lời:


7

Giải pháp thứ hai của bạn bằng cách sử dụng LEFT THAM GIA là cách tiếp cận tốt nhất. Tôi sẽ không sử dụng bảng tạm thời, tôi sẽ sử dụng bảng thông thường và điền vào bảng với các giá trị mới bất cứ khi nào bạn muốn chạy truy vấn.


5

Nghe có vẻ như "tập hợp số nguyên lớn" vẫn nhỏ hơn đáng kể so với bảng có "hàng trăm ngàn số nguyên". Với giả định đó và trừ khi MySQL có cách sử dụng một mảng các số nguyên của bạn làm bảng trong câu lệnh SQL của bạn, tùy chọn thứ hai của bạn có lẽ là tốt nhất. Nó sẽ thực hiện quét toàn bộ bảng tạm thời và chỉ mục trên bảng chính. Lợi ích chính là nó chỉ phải quét chỉ mục chứa hàng trăm ngàn số nguyên một lần và chỉ phải gửi cho khách hàng kết quả. Truy vấn của bạn có thể (nhưng không cần phải viết lại) như sau:

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

Tôi không xác nhận một bảng tạm thời trên một bảng thông thường vì tôi không có kiến ​​thức về sự khác biệt trên nền tảng MySQL. Trong Oracle, một bảng tạm thời có thể là tốt nhất, nhưng sau đó trong Oracle bạn sẽ chỉ sử dụng một mảng như một bảng và tham gia trực tiếp vào nó.
Leigh Riffel

3

Thay vì một bảng tạm thời và chèn vào insert into id_key_table_temp values (1),(2),(3),...,(500),(501);, bạn có thể xây dựng một truy vấn con với tất cả các giá trị bạn đang cố kiểm tra:

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

Như đã lưu ý trong nhận xét của tôi, điều này có lẽ phù hợp hơn với stackoverflow. Tuy nhiên, tôi nghĩ rằng cả hai giải pháp đó đều không phải là tốt nhất:

Giải pháp 1 yêu cầu nhiều cuộc gọi chọn, rất không hiệu quả

Giải pháp 2 tốt hơn, nhưng tôi không chắc chi phí chèn nhiều giá trị là giải pháp tốt nhất.

Một giải pháp khả thi 3 sẽ được thực hiện một truy vấn:

SELECT DISTINCT id_key FROM id_key_table

và lập trình nhận được sự khác biệt từ tập số nguyên của bạn và những gì trong DB. Tệ nhất là (vì nó có rất nhiều số nguyên) Tuyến này sẽ tốt hơn Giải pháp 1. Giải pháp 2 có khả năng CSONG trả lại rất nhiều số nguyên (nếu bảng có một bó không có trong tập dữ liệu của bạn), vì vậy nó phụ thuộc ™!


Tôi không phải là người hâm mộ giải pháp này vì kết quả sẽ rất lớn.
Clinton

@Clinton đúng, nhưng nó cũng có thể rất lớn trong giải pháp thứ hai của bạn, nếu bạn không cung cấp đủ số nguyên để lọc ra.
Derek Downey

2

Tôi đã giải quyết khá nhiều vấn đề này trong StackOverflow , nhưng tôi muốn giải thích thêm về việc sử dụng bảng tạm thời (PermTemp). ( tạm thời, không phải là một oxymoron sao?)

Trong StackOverflow , tôi đã có thủ tục lưu trữ test.CreateSampleTable và test.GetMissingIntegers tạo một bảng mẫu và sau đó tạo một bảng tạm thời động để cư trú trước khi thực hiện THAM GIA lớn để tìm sự khác biệt.

Lần này, hãy tạo bảng mẫu cùng với bảng bảng cố định.

Đây là 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 ;

Sau khi chạy này, đây là các bảng và nội dung của chúng:

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)

Dưới đây là các Triggers cho bảng 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 ;

Bây giờ, hãy nhập một lô bản ghi mới, bảng test.weekly_batch, một số khóa được sử dụng trước đó, các khóa khác được đánh dấu mới:

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

Hãy lấy test.weekly_batch và hợp nhất nó một cách an toàn vào test.id_key_table_keys và tạo bảng 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 ;

Đây là kết quả:

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

Từ thời điểm này, chỉ cần sử dụng bảng new_keys_to_load làm danh sách các khóa đánh dấu thương hiệu mới để nhập. Vì new_keys_to_load nhỏ hơn bảng PermTemp, bạn nên luôn luôn sử dụng new_keys_to_load ở phía bên trái của TRÁI PHIẾU.


Tôi đã trả lời điều này trên SO rồi
RolandoMySQLDBA
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.