2011-07-05 30 views
5

Tôi có một bảng có mã và một bảng khác có tiền tố. Tôi cần phải khớp với tiền tố (dài nhất) cho mỗi mã.Thực hành tốt nhất của MySQL: các tiền tố phù hợp

Ngoài ra còn có phạm vi phụ trong đó tôi phải hạn chế tiền tố (điều này bao gồm việc đưa vào các bảng khác). Tôi không nghĩ rằng điều này sẽ quan trọng trong hầu hết các trường hợp, nhưng đây là một đơn giản hóa (bình thường) chương trình (Tôi phải thiết item.prefix_id):

group (id) 
subgroup (id, group_id) 
prefix (id, subgroup_id, prefix) 
item (id, group_id, code, prefix_id) 

Đó là Allright để cache độ dài của tiền tố trong một lĩnh vực mới và chỉ mục nó. Nó là hoàn toàn để cache group_id trong bảng tiền tố (mặc dù các nhóm là các bảng khá nhỏ, trong hầu hết các trường hợp, tôi không nghĩ rằng bất kỳ tăng hiệu suất đạt được). mục bảng chứa một vài trăm ngàn hồ sơ, tiền tố chứa ít nhất 500.

Edit:

Xin lỗi Nếu câu hỏi không được định nghĩa đủ. Khi sử dụng từ "tiền tố" tôi thực sự có nghĩa là nó, vì vậy các mã số phải bắt đầu với tiền tố thực tế.

subgroup 
id group_id 
------------- 
1 1 
2 1 
3 1 
4 2 

prefix 
id subgroup_id prefix 
------------------------ 
1 1   a 
2 2   abc 
3 2   123 
4 4   abcdef 

item 
id group_id  code prefix_id 
----------------------------------- 
1 1   abc123 NULL 
2 1   abcdef NULL 
3 1   a123 NULL 
4 2   abc123 NULL 

Kết quả dự kiến ​​cho cột tiền tố là (item.id, item.prefix_id):

(1, 2) Bởi vì: subroups 1, 2, 3 dưới nhóm 1, mã abc123 bắt đầu bằng tiền tố a và tiền tố abcabc là logest của hai, vì vậy chúng tôi lấy id là abc là 2 và đặt nó vào item.prefix_id.

(2, 2) Bởi vì: mặc dù tiền tố {4} (là abcdef) là tiền tố phù hợp logest, nhóm con của nó (là 4) nằm dưới nhóm 2 nhưng mục nằm dưới nhóm 1, vì vậy chúng tôi có thể chọn từ các nhóm con 1, 2, 3 và vẫn là abc là kết quả phù hợp nhất trong ba tiền tố có thể.

(3, 1) Bởi vì: a là kết quả phù hợp nhất.

(4, NULL) Bởi vì: mục 4 là thuộc nhóm 2 và tiền tố chỉ thuộc nhóm 2 là abcdef đó là không phù hợp để abc123 (vì abc123 không bắt đầu bằng abcdef).

Nhưng khi tôi nói toàn bộ điều dò dẫm là không phải là phần thiết yếu của câu hỏi. Mối quan tâm chính của tôi là so khớp một bảng có các tiền tố có thể với một chuỗi các chuỗi và cách thực hiện theo cách cách tốt nhất. (Tốt nhất có nghĩa là một sự cân bằng tối ưu giữa khả năng đọc, bảo trì và hiệu suất - do đó là 'prectice tốt nhất' trong tiêu đề).

Hiện nay tôi đang làm một cái gì đó như:

UPDATE item USE INDEX (code3) 
    LEFT JOIN prefix ON prefix.length=3 AND LEFT(item.code,3)=prefix.prefix 
    LEFT JOIN subgroup ON subgroup.id=prefix.subgroup_id 
WHERE subgroup.group_id == item.group_id AND 
    item.segment_id IS NULL 

đâu code3 là một KEY code3 (segment_id, group_id, code(3)). - Và cùng một logic được lặp lại với 1, 2, 3 và 4 là chiều dài. Nó có vẻ khá hiệu quả, nhưng tôi không thích sự hiện diện của sự trùng lặp trong nó (4 truy vấn cho một hoạt động đơn lẻ). - tất nhiên điều này xảy ra trong trường hợp độ dài tối đa của các tiền tố là 4.

Cảm ơn mọi người đã chia sẻ ý tưởng của bạn cho đến nay.

+0

Bạn đã thử truy vấn gì? –

+0

Bạn làm gì với hai tiền tố cùng độ dài? –

+0

@vbence Loại cột 'mã' là gì? nếu varchar, sau đó những gì chiều dài của varchar? Và cùng một câu hỏi về tiền tố. – Karolis

Trả lời

2

Bạn hoàn toàn có thể nhớ cache bảng nhóm_id trong tiền tố.

Vì vậy, hãy tạo cột group_id trong bảng tiền tố và điền vào các cột với các giá trị thích hợp. Tôi cho rằng bạn biết cách làm điều này, vì vậy hãy chuyển sang bước tiếp theo.

Lợi ích hiệu suất lớn nhất, chúng tôi sẽ nhận được từ composit chỉ số này:

ALTER TABLE `prefix` ADD INDEX `c_index` (
    `group_id` ASC, 
    `prefix` ASC 
); 

CẬP NHẬT tuyên bố:

UPDATE item i 
SET 
    prefix_id = (
     SELECT p.id 
     FROM prefix p USE INDEX (`c_index`) 
     WHERE 
      p.group_id = i.group_id AND 
      p.prefix IN (
       LEFT(i.code, 4), 
       LEFT(i.code, 3), 
       LEFT(i.code, 2), 
       LEFT(i.code, 1) 
      )     
     ORDER BY LENGTH(p.prefix) DESC 
     LIMIT 1   
    ) 

Trong ví dụ này tôi cho rằng tiền tố có thể thay đổi chiều dài { 1,4}. Tôi đã quyết định sử dụng mệnh đề IN thay vì LIKE để nhận được toàn bộ lợi ích của c_index.

+0

Tôi đã thêm một số dữ liệu mẫu để làm rõ vấn đề. – vbence

+0

Tôi nghĩ rằng bạn đang rất gần với nhu cầu vbence cho truy vấn cập nhật của mình. Một vấn đề mặc dù. Truy vấn của bạn chỉ lấy theo ID nhóm mà không liên quan đến văn bản phù hợp từ "Tiền tố.Prefix = mục.Mã "(ví dụ: Mã mục phải bắt đầu bằng cùng giá trị như Prefix.prefix nó được nối với ...) Sửa lỗi đó và tôi nghĩ bạn có những thứ cần thiết. – DRapp

+0

@DRapp Vâng, nhưng nó không đơn giản như vậy vì sửa đổi sẽ không cho phép chúng tôi sử dụng chỉ mục cho ** ORDER BY **. – Karolis

1

Trừ khi tôi là quá đơn giản hoá, nên càng đơn giản như ... Bắt đầu một pre-truy vấn bên trong để có được những tiền tố dài nhất (không phân biệt nếu nhiều có cùng độ dài mỗi mã)

select 
     PreQuery.Code, 
     P2.ID, 
     P2.SubGroup_ID, 
     P2.Prefix 
    From 
     (select 
       i.code, 
       max(length(trim(p.Prefix))) as LongestPrefix 
      from 
       item i 
       join prefix p 
        on i.prefix_id = p.id 
      group by 
       i.code) PreQuery 
     Join item i2 
     on PreQuery.Code = i2.Code 
     Join Prefix P2 
      on i2.Prefix_ID = P2.ID 
      AND PreQuery.LongestPrefix = length(trim(P2.Prefix))) 

Bây giờ, nếu bạn muốn làm một cái gì đó đặc biệt về những nơi có nhiều với cùng một độ dài tiền tố, nó sẽ cần một số điều chỉnh, nhưng điều này sẽ nhận được nó cho bạn.

+0

'item.prefix_id' có giá trị NULL, nhiệm vụ là đặt giá trị của nó. – vbence

+0

@vbence, sau đó bạn có thể cung cấp một số ít các hàng của mỗi bảng tương ứng để hiển thị những gì bạn CÓ không ??? – DRapp

+0

Mọi trường đều có giá trị chính xác ngoại trừ item.prefix_id - Như tôi đã viết trong bài đăng gốc: "Tôi phải đặt item.prefix_id". Tôi sẽ cung cấp thêm một số thông tin vào buổi sáng. – vbence

1

Để trả lời lại vì bạn đang cố gắng cập nhật phần tử, hãy thử truy vấn cập nhật sau. Bây giờ đây là bắt xung quanh này ... "PreQuery" sẽ thực sự trả về tất cả các tiền tố phù hợp cho một mục nhất định ... Tuy nhiên, vì thứ tự dựa trên độ dài tiền tố, cho những mục có nhiều hơn một "tiền tố" phù hợp , trước tiên nó sẽ được cập nhật bằng tiền tố ngắn nhất, sau đó nhấn vào bản ghi với tiền tố dài hơn tiếp theo và cuối cùng kết thúc bằng bất kỳ giá trị nào dài nhất cho kết quả phù hợp. Vì vậy, cuối cùng, nó NÊN sẽ giúp bạn có được những gì bạn cần.

Điều đó đang được nói (và bây giờ tôi không thể kiểm tra cụ thể), nếu nó chỉ cập nhật dựa trên mục FIRST được tìm thấy cho một ID nhất định, thì chỉ cần thực hiện thứ tự theo thứ tự DESCENDING của độ dài tiền tố.

update Item, 
      (SELECT 
        I.ID, 
        P.ID Prefix_ID, 
        P.Prefix, 
        I.Code, 
        LENGTH(TRIM(P.Prefix)) as PrefixLen 
       FROM 
        Item I 
         JOIN SubGroup SG 
         ON I.Group_ID = SG.Group_ID 
          JOIN Prefix P 
           ON SG.ID = P.SubGroup_ID 
           AND LEFT(P.Prefix, LENGTH(TRIM(P.Prefix))) 
           = LEFT(I.Code, LENGTH(TRIM(P.Prefix))) 
       ORDER BY 
        I.ID, 
        LENGTH(TRIM(P.Prefix)) ) PreQuery 
     set 
     Prefix_ID = PreQuery.Prefix_ID 
     where 
     ID = PreQuery.ID 
+0

@vbence, bạn đã có cơ hội thử giải pháp này chưa ... – DRapp

Các vấn đề liên quan