2016-12-05 53 views
0

我的問題是不完全的,但類似的選擇數據行,對這個問題如何從一個逗號分隔值字段

How to SELECT parts from a comma-separated field with a LIKE statement

,但我還沒有看到任何回答有。所以我再次發佈我的問題。

我有以下表

╔════════════╦═════════════╗ 
║ VacancyId ║ Media  ║ 
╠════════════╬═════════════╣ 
║  1  ║ 32,26,30 ║ 
║  2  ║ 31, 25,20 ║ 
║  3  ║ 21,32,23 ║ 
╚════════════╩═════════════╝ 

我想選擇誰擁有媒體ID = 30或媒體= 21或媒體=數據40在這種情況下

所以輸出將返回第一個和第三排。

我該怎麼做?

我嘗試過像'30'這樣的媒體,但是沒有返回任何值。另外我只是不需要搜索該字段中的一個字符串。

我的數據庫是SQL Server的

謝謝

+0

編輯通過添加預期輸出你的問題。 –

+0

感謝您的回覆。我爲我的預期輸出添加了該行。 – Bashabi

+0

您嘗試使用媒體像「%30%」還是媒體像「%21%」或「媒體像」「%40%」 –

回答

1

如果您確信包含字符串30任何Media值將是一個你想回來,你只需要在你的LIKE聲明通配符:

SELECT * 
FROM Table 
WHERE Media LIKE '%30%' 
記住

熊雖然,這也將返回一個紀錄的298,300,302例如媒體價值,因此,如果這是給你的問題,喲日子會把你需要考慮更復雜的方法,如:

SELECT * 
FROM Table 
WHERE Media LIKE '%,30,%' 
OR Media LIKE '30,%' 
OR Media LIKE '%,30' 
OR Media = '30' 

如果有可能在串空間(根據你的問題),你也想要去除這些了:

SELECT * 
FROM Table 
WHERE REPLACE(Media,' ','') LIKE '%,30,%' 
OR REPLACE(Media,' ','') LIKE '30,%' 
OR REPLACE(Media,' ','') LIKE '%,30' 
OR REPLACE(Media,' ','') = '30' 

編輯:其實我更喜歡Coder of Code的解決方案是:

SELECT * 
FROM Table 
WHERE ',' + LTRIM(RTRIM(REPLACE(Media,' ',''))) + ',' LIKE '%,30,%' 

你提到將要搜索的THI多個字符串能量場,這也是可能的:

SELECT * 
FROM Table 
WHERE Media LIKE '%30%' 
    OR Media LIKE '%28%' 

SELECT * 
FROM Table 
WHERE Media LIKE '%30%' 
    AND Media LIKE '%28%' 
+0

嗨,謝謝你的回覆。這工作。但是有一個限制。如果我寫''4%''這樣的媒體,那麼它將與4,14,24,34,44一樣。如何克服這個限制? – Bashabi

+0

如果媒體值是'300,281'那麼它不會工作 –

+0

@Bashabi是的,我在我的答案中註明了這一點。我現在將更新以展示一種方式,您可以更復雜並更好地處理這一問題。 – 3N1GM4

3

這是從來沒有很好的使用逗號分隔值在數據庫中存儲,如果它是可行的嘗試,使單獨的表來存儲它們作爲最有可能這是1:n關係。

如果這是不可行的則有以下幾種可能的方式,你可以做到這一點, 如果你的價值觀相匹配的數量要留一樣,那麼你可能要與這取決於OR/AND一起做Like聲明系列你的要求。

Ex。 -

WHERE 
    Media LIKE '%21%' 
    OR Media LIKE '%30%' 
    OR Media LIKE '%40%' 

然而上面的語句將可能趕上它包含21所以即使與像1210值的列,210也將返回的所有值。爲了克服這一點,你可以做下面的技巧這是因爲它在where子句使用功能和違背使Seargable查詢妨礙性能。 但這裏有雲,

--Declare valueSearch variable first to value to match for you can do this for multiple values using multiple variables. 

Declare @valueSearch = '21' 

-- Then do the matching in where clause 
WHERE 
    (',' + RTRIM(Media) + ',') LIKE '%,' + @valueSearch + ',%' 

如果匹配值的數量要改變,那麼你可能想看看FullText Index,你應該考慮是相同的。 如果你決定去與這個Fulltext Index後,你可以做如下得到你想要的東西,

Ex.-

WHERE 
    CONTAINS(Media, '"21" OR "30" OR "40"') 
+0

有問題,當搜索「4」時,你也會匹配「3,49,69」。爲了解決這個問題,列表本身應該以'',''開頭和結尾,並且搜索值應該做同樣的...「,3,48,69,'LIKE'%,4,%''。也就是說,你說得對,數據結構很差,應該「正常化」。 – MatBailie

+0

@MatBailie剛剛更新了答案。乍一看錯過了它。 –

+0

「CONTAINS」同樣適用 – MatBailie

1

你可以利用這一點,但性能差不可避免。正如其他人所說,你應該使這種結構正常化。

WHERE 
     ',' + media + ',' LIKE '%,21,%' 
    OR ',' + media + ',' LIKE '%,30,%' 
    Etc, etc... 
2

儘可能最好的方式,我可以建議是首先你必須使用This link做逗號分隔值表,你將結束與表看起來像下面。

enter image description here

SELECT * FROM Table 
WHERE Media in('30','28') 

它肯定會工作。

+2

建議的結構是正確的,它應該如何建模*(儘管我還規定媒體字段是INT)*。但是結果查詢不再需要使用LIKE ...'media IN(21,30,40)'就足夠了。 – MatBailie

1

我同意不存儲這樣一個好主意逗號分隔值。如果你不得不;

我認爲使用內聯函數將提供更好的性能;

Select VacancyId, Media from (
    Select 1 as VacancyId, '32,26,30' as Media 
    union all 
    Select 2, '31,25,20' 
    union all 
    Select 3, '21,32,23' 
) asa 
CROSS APPLY dbo.udf_StrToTable(Media, ',') tbl 
where CAST(tbl.Result as int) in (30,21,40) 
Group by VacancyId, Media 

輸出是;

VacancyId Media 
----------- --------- 
1   32,26,30 
3   21,32,23 

和我們的內聯函數腳本是;

if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[udf_StrToTable]') and xtype in (N'FN', N'IF', N'TF')) 
drop function [dbo].udf_StrToTable 
GO 

CREATE FUNCTION udf_StrToTable (@List NVARCHAR(MAX), @Delimiter NVARCHAR(1)) 
RETURNS TABLE 
With Encryption 
AS 
RETURN 
( WITH Split(stpos,endpos) 
    AS(
     SELECT 0 AS stpos, CHARINDEX(@Delimiter,@List) AS endpos 
     UNION ALL 
     SELECT CAST(endpos+1 as int), CHARINDEX(@Delimiter,@List,endpos+1) 
      FROM Split 
      WHERE endpos > 0 
    ) 
    SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) as inx, 
      SUBSTRING(@List,stpos,COALESCE(NULLIF(endpos,0),LEN(@List)+1)-stpos) Result 
    FROM Split 
) 
GO 
1

該解決方案使用一個遞歸CTE來識別字符串內的每個逗號的位置,然後使用SUBSTRING返回逗號之間所有字符串。

我留下了一些不必要的代碼到位,以幫助你得到你的頭一輪它在做什麼。你可以把它剝下來,以提供你所需要的。

DROP TABLE #TMP 
CREATE TABLE #TMP(ID INT, Vals CHAR(100)) 

INSERT INTO #TMP(ID,VALS) 
VALUES 
(1,'32,26,30') 
,(2,'31, 25,20') 
,(3,'21,32,23') 

;WITH cte 
    AS 
    (
    SELECT 
     ID 
     ,VALS 
     ,0 POS 
     ,CHARINDEX(',',VALS,0) REM 
    FROM 
     #TMP 
    UNION ALL 
     SELECT ID,VALS,REM,CHARINDEX(',',VALS,REM+1) 
     FROM 
     cte c 
     WHERE CHARINDEX(',',VALS,REM+1) > 0 
    UNION ALL 
     SELECT ID,VALS,REM,LEN(VALS) 
     FROM 
     cte c 
     WHERE POS+1 < LEN(VALS) AND CHARINDEX(',',VALS,REM+1) = 0 
    ) 
,cte_Clean 
    AS 
    (
    SELECT ID,CAST(REPLACE(LTRIM(RTRIM(SUBSTRING(VALS,POS+1,REM-POS))),',','') AS INT) AS VAL FROM cte 
    WHERE POS <> REM 
    ) 
SELECT 
    ID 
FROM 
    cte_Clean 
WHERE 
    VAL = 32 
ORDER BY ID