2010-05-30 36 views
5

Tôi có bảng di sản mysql chứa một mã định danh ứng dụng khách và một danh sách các mục, sau này là chuỗi được phân cách bằng dấu phẩy. Ví dụ. "xyz001", "foo,bar,baz". Đây là công cụ kế thừa và người dùng khẳng định có thể chỉnh sửa chuỗi phân cách bằng dấu phẩy.Chuyển chuỗi ký tự phân tách thành nhiều giá trị trong mysql

Hiện tại, họ có yêu cầu cho bảng báo cáo với các báo cáo ở trên được chia thành các hàng riêng biệt, ví dụ:

"xyz001", "foo" 
"xyz001", "bar" 
"xyz001", "baz" 

Phá vỡ chuỗi thành chuỗi con có thể dễ dàng thực hiện được và tôi đã viết một thủ tục để thực hiện điều này bằng cách tạo ra một bảng riêng biệt, nhưng điều đó đòi hỏi phải kích hoạt để đối phó với xóa, cập nhật và chèn. Truy vấn này hiếm khi được yêu cầu (giả sử mỗi tháng một lần) nhưng phải được cập nhật hoàn toàn khi nó được chạy, ví dụ: chi phí của các trigger không được đảm bảo và các nhiệm vụ được lập biểu để tạo bảng có thể không đủ kịp thời.

Có cách nào để viết một hàm trả lại bảng hoặc tập hợp để tôi có thể tham gia mã định danh với các mục riêng lẻ theo yêu cầu không?

Trả lời

3

Điều này được gọi là walking a string. Dưới đây là ví dụ về cách bạn có thể thực hiện với thông số được cung cấp:

Bạn sẽ cần phải tạo một bảng chứa nhiều số nguyên theo chiều dài của trường + 1. Vì vậy, nếu chiều dài của trường là 255, bạn sẽ cần 256 bản ghi mà chỉ chứa một số duy nhất từ ​​0-255.

int_table:

+---+ 
| i | 
+---+ 
| 0 | 
| 1 | 
| 2 | 
| 3 | 
| 4 | 
| 5 | 
| 6 | 
+---+ 

Tiếp theo, bạn sẽ cần một truy vấn mà gia nhập vào bảng này và kiểm tra nếu một dấu phẩy tồn tại trong vị trí đó hay không. (Tôi gọi là bảng legacy_table với các lĩnh vực clientitems, tương ứng.)

select 
    legacy_table.client, 
    substring(
    legacy_table.items, 
    int_table.i + 1, 
    if(
     locate(',', legacy_table.items, int_table.i + 1) = 0, 
     length(legacy_table.items) + 1, 
     locate(',', legacy_table.items, int_table.i + 1) 
    ) - (int_table.i + 1) 
) as item 
from legacy_table, int_table 
where legacy_table.client = 'xyz001' 
    and int_table.i < length(legacy_table.items) 
    and (
    (int_table.i = 0) 
    or (substring(legacy_table.items, int_table.i, 1) = ',') 
) 

Nó có thể không đủ hiệu quả để bạn có thể thực sự sử dụng nó, nhưng tôi nghĩ rằng tôi muốn tặng nó như là một ví dụ chỉ để bạn biết những gì có sẵn.

+0

Điều này làm việc một điều trị, không thể nói tôi hoàn toàn hiểu nó nhưng tôi chắc chắn sẽ nghiên cứu nó cho đến khi tôi làm. Cảm ơn nhiều. – epo

+0

Cảnh báo: mã trên có chứa lỗi. Nó không trả lại phần tử đầu tiên trong danh sách (ví dụ: "foo"). Mã dưới đây từ Thomas không chứa lỗi này. – GregW

0

Thú vị.

Tôi không thể nghĩ ra giải pháp sạch sẽ và ngăn nắp, nhưng tôi có thể đưa ra một số ý tưởng.

Tôi đoán bạn có thể kết hợp UNION như sau:

http://blog.fedecarg.com/2009/02/22/mysql-split-string-function/

Nếu bạn biết số lượng tối đa của các mục trong bất kỳ liên tiếp, sau đó bạn có thể làm một công đoàn về kết quả .. cái gì đó như:

(SELECT col1, SPLIT_STR(col2, ',', 1) c2 from tbl) 
UNION (SELECT col1, SPLIT_STR(col2, ',', 2) c2 from tbl where col2 like '%,%') 
UNION (SELECT col1, SPLIT_STR(col2, ',', 3) c2 from tbl where col2 like '%,%,%') 
... 

Tôi không phải là rất lên trên thủ tục lưu trữ mysql, nhưng có lẽ bạn có thể làm một số công cụ thông minh với vòng để làm cho điều này năng động hơn.

Tôi hy vọng điều này giúp bạn đi đúng hướng -?

2

Bạn có thể làm điều này với một số hoặc bàn Tally, trong đó có một danh sách tuần tự của các số nguyên:

Select Substring(T.List, N.Value, Locate(', ', T.List + ', ', N.Value) - N.Value) 
From Numbers As N 
    Cross Join MyTable As T 
Where N.Value <= Len(T.List) 
    And Substring(', ' + T.List, N.Value, 1) = ', ' 

Trong trường hợp trên, bảng Numbers của tôi được cấu trúc như sau:

Create Table Numbers(Value int not null primary key) 
+0

Để mã trên hoạt động trong MySQL trên các thẻ liên kết với câu hỏi này, bạn sẽ cần sử dụng hàm CONCAT (A, B) của MySQL thay vì cách tiếp cận A + B sử dụng toán tử + của Microsoft SQL Server để nối chuỗi. – GregW

0

tôi thực sự muốn có được điều này để làm việc bằng cách sử dụng SQL nhưng không thể. Tôi đã viết kịch bản PHP nhanh này để thực hiện công việc.

Nhanh chóng và dơ bẩn và KHÔNG làm cách nào tôi khuyên bạn nên làm mã sản xuất. Nhưng đôi khi bạn làm những gì nó cần để hoàn thành công việc nhanh chóng.

<?php 
$table = "ref_app"; 
$pri_column = "Repo"; 
$column = "Topics"; 
$newTable = "topics"; 

$conn = mysql_connect("localhost", "dev", "password"); 
mysql_select_db("esb_data"); 
if($conn==null){ 
    echo "Connection not made"; 
    exit; 
} 

$result = mysql_query("select ".$pri_column.", ".$column." from ".$table); 
if(mysql_errno()){ 
    echo "<br>".mysql_error(); 
    exit; 
} 

$applications = array(); 
while($row = mysql_fetch_array($result)){ 
    echo "<br>".$row[$pri_column]; 
    $topics = explode(",", $row[$column]); 
    foreach($topics as $topic){ 
     $topic = trim($topic); 
     $applications[$row[$pri_column]][$topic] = $topic; 
    } 
    echo "<br>".$row[$column]; 
} 
echo "<pre>"; 
print_r($applications); 
echo "</pre>"; 

foreach($applications as $app => $topics){ 
    foreach($topics as $topic){ 
     $query = "insert into ".$newTable." values ('', \"".$app."\", \"".$topic."\")"; 
     echo "<br>".$query; 
     mysql_query($query); 
     if(mysql_errno()){ 
      echo "<br>".mysql_error(); 
     } 
    } 
} 
?> 
Các vấn đề liên quan