Sql-Server

比較兩個表中的字元串 SQL Server 2014

  • February 5, 2019

我在 SQL Server 2014 數據庫中使用這兩個表:

CREATE TABLE #FORMULAS 
(
   [ID] [int] IDENTITY(1,1) NOT NULL,
   [DataSourceAddress] [nvarchar](255) NULL,
   [Description] [nvarchar](255) NULL
)

CREATE INDEX [IX_FORMULAS] 
ON #FORMULAS ([DataSourceAddress] ASC)

CREATE TABLE #PARAMETERS
(
   [ID] [int] IDENTITY(1,1) NOT NULL,
   [Datetime] DateTime NULL,
   [ParameterName] [nvarchar](255) NULL,
   [Value] float NULL
)

CREATE INDEX [IX_PARAMETERS] 
ON #PARAMETERS([ParameterName] ASC)

INSERT INTO #FORMULAS ([DataSourceAddress], [Description])
VALUES ('(parameter1+parameter2+parameter3)/3', 'Formula description 1'),
      ('parameter4', 'Formula description 2'),
      ('(parameter5*1000)', 'Formula description 3');

INSERT INTO #PARAMETERS ([Datetime], [ParameterName], [Value])
VALUES ('2016-01-26 11:40:00.000', 'parameter1', 1),
      ('2016-01-26 11:40:00.000', 'parameter2', 2),
      ('2016-01-26 11:40:00.000', 'parameter3', 3),
      ('2016-01-26 11:40:00.000', 'parameter4', 4),
      ('2016-01-26 11:40:00.000', 'parameter5', 5),
      ('2016-01-26 11:40:00.000', 'parameter6', 6),
      ('2016-01-26 11:44:00.000', 'parameter1', 1),
      ('2016-01-26 11:44:00.000', 'parameter2', 2),
      ('2016-01-26 11:44:00.000', 'parameter3', 3),
      ('2016-01-26 11:44:00.000', 'parameter4', 4),
      ('2016-01-26 11:44:00.000', 'parameter5', 5),
      ('2016-01-26 11:44:00.000', 'parameter6', 6);

我想選擇與表格#FORMULAS的列匹配[ParameterName]的表格參數#PARAMETERS

例如,取表的值 ‘(parameter1+parameter2+parameter3)/3’並接收包含#FORMULAS的表的所有行。#PARAMETERS``'parameter1','parameter2','parameter3'

我正在使用以下選擇:

SELECT  
   A.[Datetime],
   A.[ParameterName],
   B.[Description],
   A.[Value]
FROM   
   #PARAMETERS A
JOIN 
   #FORMULAS B ON CHARINDEX(A.[ParameterName], B.[DataSourceAddress]) > 0

#PARAMETERS表有超過 160000 行(有 1174 個唯一的ParameterNames),#FORMULAS有 3283 行。查詢需要很長時間才能完成,超過 10 分鐘。

有沒有更快的查詢方法?

具有良好性能的萬用字元匹配字元串總是很棘手,但這是一個盡力而為的嘗試。

首先,您的索引可以改進。#PARAMETERS 上的索引僅包含實際ParameterName列,如果您需要參數列表,這很好,但是當您需要任何其他列時,索引對 SQL Server 將無用,它會恢復為掃描整個表. 我建議通過將索引更改為聚集索引來解決此問題:

CREATE UNIQUE CLUSTERED INDEX [IX_PARAMETERS]
   ON #PARAMETERS (ParameterName, [Datetime])
   WITH (DROP_EXISTING=ON);     -- Remove this row if the index doesn't already exist.

聚集索引包括表中的每一列,這會自動使聚集索引成為覆蓋索引

從這裡,我更新了您的查詢以執行三件事:

  • 收集#PARAMETERS 中的每個唯一參數。
  • 對於每個唯一參數,在#FORMULAS 中查找匹配的公式。這是昂貴的部分,並且沒有索引會真正改善這種搜尋,因為搜尋條件 (LIKE) 不是sargable。這意味著,無論是否有索引,SQL Server 都會針對每個參數遍歷表中的每一行(所謂的掃描)。
  • 最後,加入#PARAMETERS 以獲取所有日期和值。這是一種相對便宜和有效的操作。這就是#PARAMETERS 上修改後的索引有幫助的地方。

這是結果查詢:

SELECT  A.[Datetime],
       p.[ParameterName],
       B.[Description],
       A.[Value]
FROM (SELECT DISTINCT ParameterName FROM #PARAMETERS) p
CROSS APPLY (
   SELECT *
   FROM #FORMULAS B WITH (FORCESCAN)
   WHERE B.DataSourceAddress LIKE N'%'+p.ParameterName+N'%') AS B
INNER JOIN #PARAMETERS AS A ON
   p.ParameterName=A.ParameterName;

我添加WITH (FORCESCAN)以消除關鍵查找。您可以嘗試使用或不使用此提示的查詢。

如果我有更多的時間來處理這個問題並且這是一個超級關鍵的查詢,我可能會 (a) 重塑表和/或 (b) 考慮將 #FORMULAS 中的數據作為 XML 數據儲存並添加 XML 索引,這將消除 LIKE 匹配。

腳註:即使您只是在搜尋“parameter1”,像您在此處所做的文本匹配(LIKE並且CHARINDEX在此處幾乎相同)將包括帶有參數“parameter10”和“parameter11”的公式。

引用自:https://dba.stackexchange.com/questions/127575