SQL Server Query to return a row for each separated value in column

zvms9eto  于 2023-10-15  发布在  其他
关注(0)|答案(3)|浏览(125)

I have some SQL log data like this:
| date | user | actions |
| ------------ | ------------ | ------------ |
| 2023-01-01 | dave | changed; added:apple |
| 2023-01-02 | gail | changed; removed:apple |
| 2023-01-03 | mick | changed; added:apple; removed:banana; added:cherry; removed:durian |
| 2023-01-04 | dave | changed; removed:banana; added:cherry |

I want a query that splits the actions into separate returned rows, eg actions for user dave in January

dateuseractionitem
2023-01-01daveaddedapple
2023-01-04daveremovedbanana
2023-01-04daveaddedcherry

There can be a one or many actions in the source column, like 10 or more. The actual data is a bit more complex; I've simplified to key elements. Typical queries I need would be changes for a particular user or a particular fruit in a time period.

I haven't found anything quite like this problem.

0qx6xfy6

0qx6xfy61#

As HABO said in comment, string_split() on ; then split on : using charindex() , left() & right() .

You can filter for the required in where clause

select t.[date], t.[user], a.[action], a.[item]
from   yourtable t
       cross apply string_split(actions, ';') s
       cross apply
       (
           select [action] = left(s.value, charindex(':', s.value) - 1),
                  [item]   = right(s.value, len(s.value) - charindex(':', s.value))
       ) a
where  t.[user]       = 'dave'
and    trim(s.value) <> 'changed'
9njqaruj

9njqaruj2#

Please try the following solution based on STRING_SPLIT() and PARSENAME() functions.

Assumption is that the actions column values don't contain dots.

SQL

-- DDL and sample data population, start
DECLARE @tbl TABLE (_date DATE PRIMARY KEY, [user] VARCHAR(30), actions VARCHAR(100));
INSERT @tbl (_date, [user], actions) VALUES
('2023-01-01', 'dave', 'changed; added:apple'),
('2023-01-02', 'gail', 'changed; removed:apple'),
('2023-01-03', 'mick', 'changed; added:apple; removed:banana; added:cherry; removed:durian'),
('2023-01-04', 'dave', 'changed; removed:banana; added:cherry');
-- DDL and sample data population, end

SELECT t._date, t.[user]
    , PARSENAME(t2.tokens, 2) AS [action]
    , PARSENAME(t2.tokens, 1) AS item
FROM @tbl AS t
CROSS APPLY STRING_SPLIT(actions, ';') AS t1
CROSS APPLY (SELECT REPLACE(TRIM(t1.value), ':', '.')) AS t2(tokens)
WHERE t1.value LIKE '%:%'
    AND [user] = 'dave';

Output

_dateuseractionitem
2023-01-01daveaddedapple
2023-01-04daveremovedbanana
2023-01-04daveaddedcherry
0tdrvxhp

0tdrvxhp3#

perhaps something like this

WITH SplitActions AS (
  SELECT
    [date],
    [user],
    TRIM(value) AS action_item
  FROM elg_surveyBase
  CROSS APPLY STRING_SPLIT(actions, ';')
),
ActionDetails AS (
  SELECT
    [date],
    [user],
    TRIM(SUBSTRING(action_item, 1, CHARINDEX(':', action_item) - 1)) AS action,
    TRIM(SUBSTRING(action_item, CHARINDEX(':', action_item) + 1, LEN(action_item))) AS item
  FROM SplitActions
)
SELECT
  [date],
  [user],
  action,
  item
FROM ActionDetails
WHERE [user] = 'dave'
  AND [date] BETWEEN '2023-01-01' AND '2023-01-31';

相关问题