2

我有一个带有分隔符的字符串值列,如下所示,我在 sql 查询的选择部分中使用它。

0040~0040~0040~0040~0040^00~00~00~01~05^100~001~010~011~015^00~00~00~01~05

'^'使用如下所示将各个磁头分开。

Head1 = 0040~0040~0040~0040~0040

Head2 = 00~00~00~01~05

Head3 = 100~001~010~011~015

Head4 = 00~00~00~01~05

所有 4 个头都有相同数量的'~'分隔条目(条目不一定是 5 个)。

我需要的是将所有 4 个头的第一个条目合并为一个。如下所示。

0040-00-100-00'-'

然后是第二个条目,依此类推所有条目。

如果条目数像我的字符串一样为 4,则格式化输出应如下所示(每个条目以逗号分隔):

0040-00-100-00,0040-00-001-00,0040-00-010-00,0040-01-011-01,
0040-05-015-05

我想在我获取此列值的选择查询中执行此操作。

例子..

select x,y,z,(this is where I want this changes to be done.) from abc
4

2 回答 2

1

第一个 CTE 只是创建虚拟记录的选择,您可以忽略它。第二个 CTE 是递归的,以在每个 ID 上拆分 ^ 上的列值,第三个 CTE 是组合使用每个 ID 使用 XML 分隔的部分-,最后一个 CTE 是将所有这些组合成一个由 分隔的字符串,。在组合所有内容之前,必须有一个 ID 列进行分组。它适用于 n 行。

;WITH myTable (id, MyColumn)
AS (SELECT 1, '0040~0040~0040~0040^00~00~00~01~05^100~001~010~011~015^00~00~00~01~05'
union ALL SELECT 2, 'b040~0040~0040~0040^b0~00~00~01~05^b00~001~010~011~015^b02~00~00~01~05'
union ALL SELECT 3, 'c040~0040~0040~0040^c0~00~00~01~05^c00~001~010~011~015^c02~00~00~01~05'
union ALL SELECT 4, 'd040~0040~0040~0040^d0~00~00~01~05^d00~001~010~011~015^d02~00~00~01~05'
),
RecursiveCTE (id, sub, startInd, endInd)
AS
(
    SELECT 
        id, 
        convert(varchar, SUBSTRING(MyColumn, 1, CHARINDEX('^', MyColumn ))) as sub
        , 0 AS startInd
        , CHARINDEX('^', MyColumn ) AS endInd
        from mytable
    UNION ALL
        select id,
        convert(varchar, SUBSTRING((SELECT MyColumn FROM myTable WHERE id = dr.id), endInd + 1, endInd - startInd)),
        endInd,
        CHARINDEX('^',  (SELECT MyColumn FROM myTable WHERE id = dr.id), endInd + 1) as endInd
    from RecursiveCTE dr
        where dr.endInd <> 0
),
PerIDSeperatedByDash AS (
select id,
STUFF((
select '-' + SUBSTRING(drinner.sub, 1, CHARINDEX('~', drinner.sub) - 1)
from RecursiveCTE drinner
WHERE (drinner.id = drouter.id)
FOR XML PATH ('')),1,1,'') AS DashSeperated
from RecursiveCTE drouter
group by id)
SELECT DISTINCT STUFF((
SELECT ',' + ds.DashSeperated
FROM PerIDSeperatedByDash ds
ORDER BY id
FOR XML PATH ('')),1,1,'') 
from PerIDSeperatedByDash 
于 2016-04-29T09:16:04.847 回答
1

您提出的问题不是一个容易解决的问题,将自己限制在 select 语句的内部。虽然你说你没有,但如果你知道每个字符串中有多少个“头”,你可以设置一个单一的、复杂的、很长的选择语句(你的“字符串值有分隔符”称为富吧):

SELECT x,y,z, SUBSTR(foobar, 1, INSTR(foobar, '~') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^') + 1, INSTR(foobar, '~', INSTR(foobar, '^')) - INSTR(foobar, '^') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 2) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 3) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) || ',' || SUBSTR(foobar, INSTR(foobar, '~') + 1, INSTR(foobar, '~', 1, 2) - INSTR(foobar, '~', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1)) - INSTR(foobar, '^', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) 
FROM abc

并且只获取前两个“条目”,但是它将获取每个“头”,无论长度如何,并且可以扩展以包含更多条目,但如果只有两个条目并且选择语句被扩展,它将不起作用五个条目,这是不切实际的,因为您说一行可能有任意数量的条目。有一种方法可以使用CASE语句来实现这一点,但它很长,也很复杂。以下是您可能正在寻找的示例查询:

SELECT x, y, z,
  CASE (LENGTH(SUBSTR(foobar, 1, INSTR(foobar, '^')))-LENGTH(REPLACE(SUBSTR(foobar, 1, INSTR(foobar, '^')), '~', '')) + 1)
    WHEN 1 THEN SUBSTR(foobar, 1, INSTR(foobar, '^') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 1) + 1, INSTR(foobar, '^', 1, 2) - INSTR(foobar, '^', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 2) + 1, INSTR(foobar, '^', 1, 3) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 3) + 1, LENGTH(foobar) - INSTR(foobar, '^', 1, 3))
    WHEN 2 THEN SUBSTR(foobar, 1, INSTR(foobar, '~') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^') + 1, INSTR(foobar, '~', INSTR(foobar, '^')) - INSTR(foobar, '^') - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 2) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '^', 1, 3) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) || ',' || SUBSTR(foobar, INSTR(foobar, '~') + 1, INSTR(foobar, '^', 1, 1) - INSTR(foobar, '~', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 1), 1) - INSTR(foobar, '^', 1, 1) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 2)) - INSTR(foobar, '^', 1, 2) - 1) || '-' || SUBSTR(foobar, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) + 1, INSTR(foobar, '~', INSTR(foobar, '^', 1, 3)) - INSTR(foobar, '^', 1, 3) - 1) 
    ELSE 'Too many entries: ' || (LENGTH(SUBSTR(foobar, 1, INSTR(foobar, '^')))-LENGTH(REPLACE(SUBSTR(foobar, 1, INSTR(foobar, '^')), '~', '')) + 1) || ' entries given of 2 maximum'
  END AS SAMPLE_OUTPUT
FROM abc;

上面的查询(当超出其当前的两个条目容量时)将组织您的“头”。它非常长且复杂,但是如果您知道每个头的长度将是恒定的(例如“Head1”中每个条目的长度始终为 4),那么可以缩短代码,并增加执行速度的好处, 如果值是硬编码的,没有INSTR. 下面的示例假设头部长度为 4、2、3 和 2,如您的示例数据中所示:

SELECT x, y, z,
  CASE (LENGTH(SUBSTR(foobar, 1, INSTR(foobar, '^')))-LENGTH(REPLACE(SUBSTR(foobar, 1, INSTR(foobar, '^')), '~', '')) + 1)
    WHEN 1 THEN REPLACE(foobar, '^', '-')
    WHEN 2 THEN SUBSTR(foobar, 1, 4) || '-' || SUBSTR(foobar, 11, 2) || '-' || SUBSTR(foobar, 17, 3) || '-' || SUBSTR(foobar, 25, 2) || ',' || SUBSTR(foobar, 6, 4) || '-' || SUBSTR(foobar, 14, 2) || '-' || SUBSTR(foobar, 21, 3) || '-' || SUBSTR(foobar, 28, 2) 
    WHEN 3 THEN SUBSTR(foobar, 1, 4) || '-' || SUBSTR(foobar, 16, 2) || '-' || SUBSTR(foobar, 25, 3) || '-' || SUBSTR(foobar, 37, 2) || ',' || SUBSTR(foobar, 6, 4) || '-' || SUBSTR(foobar, 19, 2) || '-' || SUBSTR(foobar, 29, 3) || '-' || SUBSTR(foobar, 40, 2) || ',' || SUBSTR(foobar, 11, 4) || '-' || SUBSTR(foobar, 22, 2) || '-' || SUBSTR(foobar, 33, 3) || '-' || SUBSTR(foobar, 43, 2)
    ELSE 'Too many entries: ' || (LENGTH(SUBSTR(foobar, 1, INSTR(foobar, '^')))-LENGTH(REPLACE(SUBSTR(foobar, 1, INSTR(foobar, '^')), '~', '')) + 1) || ' entries given of 3 maximum'
  END AS SAMPLE_OUTPUT
FROM abc;

最后一点:我建议为 case 语句使用列别名,因为如果您尝试在没有它的情况下测试代码,原因会变得非常明显。

于 2016-04-29T01:53:24.103 回答