2017-05-16 224 views
0

下面的MySQL查詢使用PHP來獲取$ sector,這是一個數字,$ subsector_text是逗號分隔的字符串。 $ subsector_text可以是單個數字或多個ID的列表,例如「3,4,7,9」。SQL查詢將逗號分隔的字符串與逗號分隔的字符串進行匹配?

$sql = " 
SELECT DISTINCT a.id 
       , a.name 
       , a.category_id 
       , a.sector 
       , a.subsector 
       , a.year_in_operation 
       , a.state 
       , a.total_value 
       , b.country_id 
       , b.project_id 
       , c.isocode_3 
       , c.name 
      FROM com_barchan_project a 
      JOIN com_barchan_location b 
      ON b.project_id = a.id 
      JOIN com_barchan_country c 
      ON c.id = b.country_id 
      JOIN com_barchan_project_value_join d 
      ON a.id = d.project_id 
      WHERE a.state = 1 
      AND a.sector = '$sector' 
      AND a.subsector REGEXP '^{$subsector_text}[,]|[,]{$subsector_text}[,]|[,]{$subsector_text}$|^{$subsector_text}$' 
      ORDER 
      BY a.total_value DESC 
       , a.category_id ASC 
       , a.name ASC 
"; 

我與上面的查詢遇到的問題是與線:

AND a.subsector REGEXP '^{$subsector_text}[,]|[,]{$subsector_text}[,]|[,]{$subsector_text}$|^{$subsector_text}$' 

如果$ subsector_text =「3,4,5,9」,那麼它只能返回該記錄$ subsector字段中恰好包含「3,4,5,9」。

想要的結果是它將返回任何具有$ subsector_text中任何值的記錄。例如,所有這些應該返回,但目前不是。這份清單只是一個例子,絕非確切的。

1,3 
1,5 
1,3,7,9 
3,5 
3,4,5,9 
9 
3 
5 
4 

如何更改查詢以選擇任何在$ subsector_text字符串中具有值的記錄?

請注意:如果$ subsector_text = 11,則不應選擇以下內容作爲示例。

1 
12 
21 

任何幫助將不勝感激。

+10

我想你已經發現了爲什麼在數據庫中使用逗號分隔值是一個超級壞主意。你可以調整你的模式,使其更符合關係數據庫約定嗎? – tadman

+0

不是沒有重寫應用程序的各個部分。 –

+1

現在你要麼解決這個問題,即使它是一個巨大的麻煩,或者你可以試着鋪平它很多很多的代碼,它們真的不應該放在第一位。這是如何發生難以克服的技術債務:拖延不可避免的。 – tadman

回答

0

的解決方案是重構該應用。它花了幾天時間,但違規代碼已經消失,並創建了一個新的子界面表。感謝大家。

2

將逗號分隔字符串中的任何值與單個謂詞中另一個逗號分隔字符串中的任何值進行匹配是不現實的。

您可以使用FIND_IN_SET()一次搜索一個值。

這意味着您需要多個謂詞,通過分割輸入$subsector_text來獲得每個值。因此,分割您的變量並將其映射到一系列FIND_IN_SET()調用中。

我沒有測試過下面的代碼,但它應該給你的想法是什麼我談論:

$subsector_array = array_map('intval', explode(',', $subsector_text)); 
$subsector_terms = array_map(
    function ($id) { return "FIND_IN_SET($id, a.subsector)"; }, 
    $subsector_array); 
$subsector_expr = implode(' OR ', $subsector_terms); 

$sql = " 
SELECT ... 
      WHERE a.state = 1 
      AND a.sector = '$sector' 
      AND ($subsector_expr) 
..."; 

當然,這將迫使表掃描,因爲沒有辦法指數FIND_IN_SET()或任何其他搜索子字符串的操作。那麼,我想你的條件a.statea.sector將使用索引來縮小搜索範圍,然後再應用FIND_IN_SET()條件。

我明白必須使用您繼承的系統進行工作的兩難困境。讓經理知道這需要在某個時候進行重構,因爲它現在的設計方式永遠不會有效或可靠。

+0

謝謝比爾。我會給它一個鏡頭。 –

0

你的方法是正確的,但需要一些修改。代替的嘗試僅在一個條件(REGEXP)來匹配,可以創建多個條件加入了與OR ...

實施例:

$subsectorArray = explode(',', $subsector_text); 
$or = []; 
foreach ($subsectorArray as $subsector){ 
    $or[] = "a.subsector REGEXP '[^[:alnum:]]{$subsector}[^[:alnum:]]|^{$subsector}[^[:alnum:]]|[^[:alnum:]]{$subsector}$|^{$subsector}$'"; 
} 
$orStr = implode(' OR ', $or); 

$sql = " 
SELECT DISTINCT a.id 
       , a.name 
       , a.category_id 
       , a.sector 
       , a.subsector 
       , a.year_in_operation 
       , a.state 
       , a.total_value 
       , b.country_id 
       , b.project_id 
       , c.isocode_3 
       , c.name 
      FROM com_barchan_project a 
      JOIN com_barchan_location b 
      ON b.project_id = a.id 
      JOIN com_barchan_country c 
      ON c.id = b.country_id 
      JOIN com_barchan_project_value_join d 
      ON a.id = d.project_id 
      WHERE a.state = 1 
      AND a.sector = '$sector' 
      AND ($orStr) 
      ORDER 
      BY a.total_value DESC 
       , a.category_id ASC 
       , a.name ASC 
"; 
+0

做或不​​做。沒有「嘗試」。一個好的答案***將總是解釋所做的事情以及爲什麼這樣做,不僅是爲了OP,還是爲了將來訪問SO。 –

+0

更新,感謝您的建議。 – rafrsr