我继承了一个包含手工输入奖励编号的列的表。它已被许多人使用多年。奖项数字一般如下:
R01AR012345-01
R01AR012345-02
R01AR012345-03
每年都会分配奖励编号。因为过去有这么多不同的人参与其中,所以这些人的输入方式并不一致。例如,奖励序列可能如下所示:
R01AR012345-01
1 RO1AR012345-02
12345-03
12345-05A1
1234506
我找到的规则是返回该列中5个连续整数与另一个记录匹配的记录。
我知道如何匹配给定的字符串,但是当5个连续的整数未知时,我会感到茫然。
这是一个样本表,可以让我更清楚地寻找:
+----------------------+
| table: AWARD |
+-----+----------------+
| ID | AWARD_NO |
+-----+----------------+
| 12 | R01AR015123-01 |
+-----+----------------+
| 13 | R01AR015124-01 |
+-----+----------------+
| 14 | 15123-02A1 |
+-----+----------------+
| 15 | 1 Ro1XY1512303 |
+-----+----------------+
| 16 | R01XX099232-01 |
+-----+----------------+
在上表中,将返回以下ID:12,13,14,15
匹配的五个连续整数是:
12,13: 01512
12,14: 15123
12,15: 15123
在我们的具体案例中,ID 13是误报......但我们愿意根据具体情况处理这些问题。
这里是上表所需的回报:
+-----+-----+----------------+----------------+
| ID1 | ID2 | AWARD_NO_1 | AWARD_NO_2 |
+-----+-----+----------------+----------------+
| 12 | 13 | R01AR015123-01 | R01AR015124-01 |
+-----+-----+----------------+----------------+
| 12 | 14 | R01AR015123-01 | 15123-02A1 |
+-----+-----+----------------+----------------+
| 12 | 15 | R01AR015123-01 | 1 Ro1XY1512303 |
+-----+-----+----------------+----------------+
现在......我可以使用误报(比如12匹配13)和重复(因为如果12匹配14,那么14也匹配12)。我们正在查看类似18,000行的内容。在这种情况下,优化并不是必需的,因为它只需要运行一次。
答案 0 :(得分:2)
这应该处理删除重复和大多数误报:
DECLARE @SPONSOR TABLE (ID INT NOT NULL PRIMARY KEY, AWARD_NO VARCHAR(50))
INSERT INTO @SPONSOR VALUES (12, 'R01AR015123-01')
INSERT INTO @SPONSOR VALUES (13, 'R01AR015124-01')
INSERT INTO @SPONSOR VALUES (14, '15123-02A1')
INSERT INTO @SPONSOR VALUES (15, '1 Ro1XY1512303')
INSERT INTO @SPONSOR VALUES (16, 'R01XX099232-01')
;WITH nums AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS [Num]
FROM sys.objects
),
cte AS
(
SELECT sp.ID,
sp.AWARD_NO,
SUBSTRING(sp.AWARD_NO, nums.Num, 5) AS [TestCode],
SUBSTRING(sp.AWARD_NO, nums.Num + 5, 1) AS [FalsePositiveTest]
FROM @SPONSOR sp
CROSS JOIN nums
WHERE nums.Num < LEN(sp.AWARD_NO)
AND SUBSTRING(sp.AWARD_NO, nums.Num, 5) LIKE '%[1-9][0-9][0-9][0-9][0-9]%'
-- AND SUBSTRING(sp.AWARD_NO, nums.Num, 5) LIKE '%[0-9][0-9][0-9][0-9][0-9]%'
)
SELECT sp1.ID AS [ID1],
sp2.ID AS [ID2],
sp1.AWARD_NO AS [AWARD_NO1],
sp2.AWARD_NO AS [AWARD_NO2],
sp1.TestCode
FROM cte sp1
CROSS JOIN @SPONSOR sp2
WHERE sp2.AWARD_NO LIKE '%' + sp1.TestCode + '%'
AND sp1.ID < sp2.ID
--AND 1 = CASE
-- WHEN (
-- sp1.FalsePositiveTest LIKE '[0-9]'
-- AND sp2.AWARD_NO NOT LIKE
-- '%' + sp1.TestCode + sp1.FalsePositiveTest + '%'
-- ) THEN 0
-- ELSE 1
-- END
输出
ID1 ID2 AWARD_NO1 AWARD_NO2 TestCode
12 14 R01AR015123-01 15123-02A1 15123
12 15 R01AR015123-01 1 Ro1XY1512303 15123
14 15 15123-02A1 1 Ro1XY1512303 15123
如果ID 14和15 不匹配,我们也可能会对此进行更正。
编辑:
根据@Serpiton的评论,我注释了[FalsePositiveTest]
字段的创建和使用,因为将SUBSTRING上的LIKE子句中的初始字符范围更改为[1-9]
实现了相同的目标,更有效率。但是,此更改假定没有有效的Award#将以0开头,我不确定这是否是一个有效的假设。因此,我保留了原始代码,但只是注释掉了。
答案 1 :(得分:0)
您希望在where子句中使用LIKE命令,并使用模式查找5个数字。请参阅此帖子here:
可能有更好的方法来表示这一点,但下面的示例在列值中的任何位置查找彼此相邻的0-9中的5位数。但这可能会很慢......
Select *
from blah
Where column LIKE '%[0-9][0-9][0-9][0-9][0-9]%'
答案 2 :(得分:0)
创建一个sql server函数来提取5个数字,然后在查询中使用该函数。
也许是这样的:
select GetAwardNumber(AwardNumberField) as AwardNumber
from Awards
group by GetAwardNumber(AwardNumberField)
答案 3 :(得分:0)
我不会发布代码,而是关于如何执行此操作的想法。
首先,您需要创建一个表值函数,该函数将返回大于5个字符的字符串中的所有数字序列。 (SO上有例子) 因此,对于每个条目,您的函数将返回一个数字列表。
之后,查询将简化为:
;with res as (
select
id, -- hopefully there is an id on your table
pattern -- pattern is from the list of patterns the udtf returns
from myTable
cross apply udtf_custom(myString) -- myString is the string you need to split
)
select
pattern
from res
group by pattern
having count(distinct id)>1
我必须注意,这是出于示例目的,应该涉及一些编码和测试,但这应该是它的故事。
祝你好运,希望有所帮助。答案 4 :(得分:0)
这是我最终的结果:
SELECT a1.ID as AWARD_ID_1, a2.ID as AWARD_ID_2, a1.AWARD_NO as Sponsor_Award_1, a2.AWARD_NO as Sponsor_Award_2
FROM AWARD a1
LEFT OUTER JOIN AWARD a2
ON SUBSTRING(a1.AWARD_NO,PATINDEX('%[0-9][0-9][0-9][0-9][0-9]%',a1.AWARD_NO + '1'),5) = SUBSTRING(a2.AWARD_NO,PATINDEX('%[0-9][0-9][0-9][0-9][0-9]%',a2.AWARD_NO + '1'),5)
WHERE
a1.AWARD_NO <> '' AND a2.AWARD_NO <> ''
AND a1.ID <> a2.ID
AND a1.AWARD_NO LIKE '%[0-9][0-9][0-9][0-9][0-9]%' AND a2.AWARD_NO LIKE '%[0-9][0-9][0-9][0-9][0-9]%'
可能五个字符的第一个子字符串可能不匹配(当它们应该生成匹配时),但它对我们足够接近。 : - )