SQL:遍历包含某些范围的列表

时间:2014-12-31 16:02:28

标签: sql-server list recursion iteration

我正在尝试获取包含在列表中的ID的产品的信息。问题是该列表包含一些单个值和一些范围值:

  

PX03 - PX069,PX20,PX202,PX25 - PX270,PX250 - PX2509,PX251,PX2511 -   PX2513

基本上我正在寻找的是获取包含值和范围的列表或字符串的一些方法,并且结束输出是一个表或列表,它具有单独的范围内的所有值,以便我可以遍历它们。

我有一个存储过程循环遍历主产品表中使用'PX'前缀的所有ID,但该表包含所有ID(即PX 1 - 9999,LX 00001 - 99999),我只想搜索上面列表中包含的内容。我可以单独写出所有id,但是一些范围包含许多值,这将耗费时间。

我的想法是创建一个包含此列表的单独表,其中将有三列:标识列,然后是一列,用于范围的开头和结尾。任何没有范围的项目只有开始和结束范围的相同值,即:

----------------------------------
rownum | range_start | range_end|
----------------------------------
1        PX03         PX069
2        PX20         PX20
3        PX202        PX202
4        PX25         PX25
5        PX250        PX2509

然后使用以下内容填充表格:

SELECT id from product_table
WHERE id BETWEEN listtable.range_start AND listtable.range_end

其中product_table是我的原始表,其中包含产品ID,其信息和listtable是我刚刚创建的新表。这会给我:

id|
---
PX03
PX030
PX031
PX032
PX033
.
.
.
PX067
PX068
PX069
PX20
PX202
PX25
PX250
PX251

但我想我需要遍历列表,我不知道该怎么做。任何想法,提示或建议?

更新

使用@asantaballa提供的解决方案创建表后,就像使用内连接一样简单:

SELECT d.id
FROM product_table d
INNER JOIN @RangeTable r
ON d.id BETWEEN r.RangeFrom AND r.RangeTo

2 个答案:

答案 0 :(得分:0)

看看这是否适用于有关将字符串转换为表格的部分。

Declare @StrList Varchar(1000) = 'PX03 - PX069, PX20, PX202, PX25 - PX270, PX250 - PX2509, PX251, PX2511 - PX2513'
Declare @RangeTable Table (RangeFrom VarChar(32), RangeTo VarChar(32))
Select @StrList = Replace(@StrList,' ', '') + ','
Declare @StrListItem Varchar(32)
While CHARINDEX(',', @StrList) > 0
Begin
    Select @StrListItem = SUBSTRING(@StrList,1,CHARINDEX(',', @StrList) - 1)
    Declare
      @RangeFrom VarChar(32)
    , @RangeTo  VarChar(32)
    If CHARINDEX('-', @StrListItem) = 0
    Begin
        Select 
          @RangeFrom = @StrListItem
        , @RangeTo = @StrListItem
    End
    Else
    Begin
        Select 
          @RangeFrom = SUBSTRING(@StrListItem, 1, CHARINDEX('-', @StrListItem) - 1)
        , @RangeTo = SUBSTRING(@StrListItem, CHARINDEX('-', @StrListItem) + 1, LEN(@StrListItem) - CHARINDEX('-', @StrListItem))
    End
    Insert Into @RangeTable (RangeFrom, RangeTo) Values (@RangeFrom, @RangeTo)
    Select @StrList = SUBSTRING(@StrList, CHARINDEX(',', @StrList) + 1, LEN(@StrList) - CHARINDEX(',', @StrList))
End
Select * From @RangeTable

答案 1 :(得分:0)

以下是您的stringproduct_table

DECLARE @STR VARCHAR(100) = 'PX03 - PX069, PX20, PX202, PX25 - PX270, PX250 - PX2509, PX251, PX2511 - PX2513'

SELECT * INTO #product_table 
FROM
(
   SELECT 'PX4' PRODID
   UNION ALL
   SELECT 'PX26' 
   UNION ALL
   SELECT 'PX75' 
   UNION ALL
   SELECT 'PX77'   
)TAB

现在创建一个表来保存值

CREATE TABLE #listtable(ROWNUM int IDENTITY(1,1),range_start VARCHAR(100),range_end  VARCHAR(100))

现在将拆分值插入表格。

INSERT INTO #listtable
SELECT  
ISNULL(PARSENAME(REPLACE(Split.a.value('.', 'VARCHAR(100)'),'-','.'),2),Split.a.value('.', 'VARCHAR(100)')) 'range_start' ,
PARSENAME(REPLACE(Split.a.value('.', 'VARCHAR(100)'),'-','.'),1) 'range_end' 
FROM  
(
     SELECT CAST ('<M>' + REPLACE(@STR, ',', '</M><M>') + '</M>' AS XML) AS Data       
) AS A 
CROSS APPLY Data.nodes ('/M') AS Split(a)

enter image description here

由于Id是字符串,因此您需要一个从Id中提取数字的函数(由SQL Server之神创建的函数 - Pinal Dave)

CREATE FUNCTION dbo.udf_GetNumeric
(@strAlphaNumeric VARCHAR(256))
RETURNS VARCHAR(256)
AS
BEGIN
DECLARE @intAlpha INT
SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric)
BEGIN
WHILE @intAlpha > 0
BEGIN
SET @strAlphaNumeric = STUFF(@strAlphaNumeric, @intAlpha, 1, '' )
SET @intAlpha = PATINDEX('%[^0-9]%', @strAlphaNumeric )
END
END
RETURN ISNULL(@strAlphaNumeric,0)
END

首先请注意,如果您提供PX1,PX2,PX3,PX4,我们将无法获得id BETWEEN listtable.range_start AND listtable.range_end因为varchar类型而非numbers。因此,我们需要从每个PX中提取数字并获取它们之间的值并附加PX

enter image description here

以下是过滤product_tablelisttable

范围内的ID的查询
;WITH CTE AS
(
   SELECT ROWNUM,CAST(dbo.udf_GetNumeric(range_start)AS INT) NUMBERS,
   CAST(dbo.udf_GetNumeric(range_end)AS INT) RTO1 
   FROM #listtable  
   UNION ALL
   SELECT T.ROWNUM,NUMBERS+1,RTO1 
   FROM #listtable T
   JOIN CTE ON CTE.ROWNUM = T.ROWNUM
   WHERE NUMBERS < RTO1
)
SELECT PRODID IDS--,ROWNUM,NUMBERS NUMS,'PX'+CAST(NUMBERS AS VARCHAR(10)) IDS2
FROM CTE
JOIN #product_table ON PRODID='PX'+CAST(NUMBERS AS VARCHAR(10))
ORDER BY NUMBERS
option (MaxRecursion 0)