我希望对T-SQL编码提出一些帮助。我有一个列有条件列表的列(WHERE语句)。我需要使用别名提取不同的列名列表。
示例数据: ISNULL(ABC.Premium,0)< 0,ISNULL(ABC.Date,101)< 19
结果: ABC.Premium,ABC.Date
我希望做一个字符串操作: 1.搜索'。' 2.字符串操作以提取名称。
我不知道如何让LOOP搜索多个'。'并提取列名称。
CREATE DATABASE TEST
GO
USE [Test]
GO
CREATE TABLE [dbo].[StringRetrival](
[ID] [int] NOT NULL,
[Condition] [varchar](4000) NULL
) ON [PRIMARY]
GO
INSERT [dbo].[StringRetrival] ([ID], [Condition]) VALUES (1, N'ISNULL(ABC.Premium, 0) < 0,ISNULL(ABC.Date, 101) < 19')
GO
INSERT [dbo].[StringRetrival] ([ID], [Condition]) VALUES (2, N'ISNULL(DEF.ColB, 101) < 25,ISNULL(DEF.ColB, 101) < 25,ISNULL(XYZ.ColB, 101) > 5, MSN.ColA < 5')
GO
INSERT [dbo].[StringRetrival] ([ID], [Condition]) VALUES (3, N'RTY.ColA')
GO
感谢您对此的帮助。
谢谢
答案 0 :(得分:2)
以下代码
LEFT
和RIGHT
剪切,直到第一个非简单字符(PATINDEX('%[^a-z,A-Z,0-9]%'
)试试这个
WITH Splitted AS
(
SELECT ID
,CAST('<x>' + REPLACE((SELECT Condition AS [*] FOR XML PATH('')),'.','</x><x>') + '</x>' AS XML) AS Part
FROM StringRetrival
)
,AllParts AS
(
SELECT ID
,ROW_NUMBER() OVER(PARTITION BY ID ORDER BY (SELECT NULL)) AS Nr
,p.value('.','nvarchar(max)') AS OnePart
FROM Splitted
CROSS APPLY Part.nodes('x') AS A(p)
)
,Parsed AS
(
SELECT ROW_NUMBER() OVER(ORDER BY ID,Nr) AS SortInx,*
FROM
(
SELECT ID,Nr,OnePart,NULL AS ColumnName
,RIGHT(OnePart,CASE WHEN Position.BreakingChar<1 THEN 999 ELSE Position.BreakingChar END) AS TableName
FROM AllParts
CROSS APPLY(SELECT PATINDEX('%[^a-z,A-Z,0-9]%',REVERSE(OnePart))-1 AS BreakingChar) AS Position
WHERE Nr=1
UNION ALL
SELECT ID,Nr,OnePart
,LEFT(OnePart,CASE WHEN Position.FirstBreakingChar<1 THEN 999 ELSE Position.FirstBreakingChar END)
,RIGHT(OnePart,CASE WHEN Position.SecondBreakingChar<1 THEN 999 ELSE Position.SecondBreakingChar END)
FROM AllParts
CROSS APPLY(SELECT PATINDEX('%[^a-z,A-Z,0-9]%',OnePart)-2 AS FirstBreakingChar
,PATINDEX('%[^a-z,A-Z,0-9]%',REVERSE(OnePart))-1 AS SecondBreakingChar) AS Position
WHERE Nr>1
) AS tbl
)
SELECT DISTINCT
p1.ID
,ISNULL(p2.TableName + '.','') + p1.ColumnName
FROM Parsed AS p1
INNER JOIN Parsed AS p2 ON p1.ID=p2.ID AND p2.Nr=p1.Nr-1;
结果
ID ColumnName
1 ABC.Date
1 ABC.Premium
2 DEF.ColB
2 MSN.Col
2 XYZ.ColB
3 RTY.ColA
答案 1 :(得分:2)
好的,这是 HIGHLY ,不适合在任何类型的生产环境中使用,但这是一个有趣的小挑战,适用于您的测试数据。我强烈建议您只需查找整个问题的替代解决方案,这会导致您在数据库表中保留where子句。
where
个字符的.
子句不起作用,也不会依赖于将字符串拆分为.
个字符的任何其他解决方案,而不需要花费太多精力检查该字符是字符串值的一部分。利用Jeff Moden's string splitting function,您可以执行以下操作:
declare @StringRetrival table(ID int,Condition varchar(4000));
insert into @StringRetrival(ID,Condition) values
(1,N'ISNULL(ABC.Premium, 0) < 0,ISNULL(ABC.Date, 101) < 19')
,(2,N'ISNULL(DEF.ColB, 101) < 25,ISNULL(DEF.ColB, 101) < 25,ISNULL(XYZ.ColB, 101) > 5, MSN.ColA < 5')
,(3,N'RTY.ColA');
with s1 as
(
select r.ID
,r.Condition
,s.ItemNumber
,max(s.ItemNumber) over (partition by r.ID) as MaxItemNumber
,reverse(s.Item) as Item
from @StringRetrival r
cross apply dbo.DelimitedSplit8K(r.Condition,'.') s
),s2 as
(
select r.ID
,r.Condition
,s.ItemNumber
,max(s.ItemNumber) over (partition by r.ID) as MaxItemNumber
,reverse(s.Item) as Item
from @StringRetrival r
cross apply dbo.DelimitedSplit8K(reverse(r.Condition),'.') s
)
select distinct s1.ID
,reverse(left(s1.Item,patindex('%[^a-zA-Z]%',s1.Item + ',')-1)) + '.' + left(s2.Item,patindex('%[^a-zA-Z]%',s2.Item + ',')-1) as Col
from s1
join s2
on s1.ID = s2.ID
and s1.ItemNumber = s2.ItemNumber
where s1.ItemNumber <> s1.MaxItemNumber
order by s1.ID;
将输出:
+----+-------------+
| ID | Col |
+----+-------------+
| 1 | ABC.Date |
| 1 | ABC.Premium |
| 2 | DEF.ColA |
| 2 | DEF.ColB |
| 2 | MSN.ColB |
| 2 | XYZ.ColB |
| 3 | RTY.ColA |
+----+-------------+
用于创建拆分功能的SQL:
CREATE FUNCTION [dbo].[DelimitedSplit8K]
--===== Define I/O parameters
(@pString VARCHAR(8000), @pDelimiter CHAR(1))
--WARNING!!! DO NOT USE MAX DATA-TYPES HERE! IT WILL KILL PERFORMANCE!
RETURNS TABLE WITH SCHEMABINDING AS
RETURN
--===== "Inline" CTE Driven "Tally Table" produces values from 1 up to 10,000...
-- enough to cover VARCHAR(8000)
WITH E1(N) AS (
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL
SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1
), --10E+1 or 10 rows
E2(N) AS (SELECT 1 FROM E1 a, E1 b), --10E+2 or 100 rows
E4(N) AS (SELECT 1 FROM E2 a, E2 b), --10E+4 or 10,000 rows max
cteTally(N) AS (--==== This provides the "base" CTE and limits the number of rows right up front
-- for both a performance gain and prevention of accidental "overruns"
SELECT TOP (ISNULL(DATALENGTH(@pString),0)) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) FROM E4
),
cteStart(N1) AS (--==== This returns N+1 (starting position of each "element" just once for each delimiter)
SELECT 1 UNION ALL
SELECT t.N+1 FROM cteTally t WHERE SUBSTRING(@pString,t.N,1) = @pDelimiter
),
cteLen(N1,L1) AS(--==== Return start and length (for use in substring)
SELECT s.N1,
ISNULL(NULLIF(CHARINDEX(@pDelimiter,@pString,s.N1),0)-s.N1,8000)
FROM cteStart s
)
--===== Do the actual split. The ISNULL/NULLIF combo handles the length for the final element when no delimiter is found.
SELECT ItemNumber = ROW_NUMBER() OVER(ORDER BY l.N1),
Item = SUBSTRING(@pString, l.N1, l.L1)
FROM cteLen l
GO