3

Table 1:

A       B       C
Test    1       This
Test1   1;4     That
Test2   7       What
Test3   6;2     Which
Test4   1;2;7   Where

Table 2:

X       Z
1       Sun
2       Mon
3       Tue
4       Wed
5       Thu
6       Fri
7       Sat

Sql:

Select
    t1.A,
    t2.Z
from
    [dbo].[Table 1] t1
    inner join [dbo].[Table2] t2
    on t1.B = t2.X

It works only for the rows that has only 1 entry in B column but fails at the 2 or more entries.

How can I modify the Sql so that it gives me the result like this:

A       Z
Test    Sun
Test1   Sun;Wed
Test2   Sat
Test3   Fri;Mon
Test4   Sun;Mon;Sat
6
  • 2
    What DB engine do you use? and second why store multiple values in a single column? That is bad table design. Can you change it? Commented Mar 31, 2017 at 16:10
  • I am sorry... Let me update my question. And I know, unfortunately this is the design that was given to us. Commented Mar 31, 2017 at 16:11
  • 1
    This isn't a good match for an RDBMS. Retrieve the data from Table 2, and perform search-and-replace in memory using a language that is better suited for the problem (Java, C#, C++). Commented Mar 31, 2017 at 16:19
  • 2
    Fix your data! Don't store lists of numeric ids as delimited string lists. That is simply the wrong way to store data in a relational database. Commented Mar 31, 2017 at 16:21
  • You might want to investigate a string splitter if you can't fix the table design. Here are some excellent choices. sqlperformance.com/2012/07/t-sql-queries/split-strings Commented Mar 31, 2017 at 16:21

2 Answers 2

1

Fun with Strings and XML, here is a little (scaled-down) technique to tokenize data.

Create Some Sample Data

Declare @Table1 table (A varchar(100),B varchar(100), C varchar(100))
Insert Into @Table1 values
('Test','1','This'),('Test1','1;4','That'),('Test2','7','What'),('Test3','6;2','Which'),('Test4','1;2;7','Where')

Declare @Table2 table (X int,Z varchar(100))
Insert Into @Table2 values
(1,'Sun'),(2,'Mon'),(3,'Tue'),(4,'Wed'),(5,'Thu'),(6,'Fri'),(7,'Sat')

The SQL

Declare @XML xml,@Str varchar(max) = (Select a,z='['+replace(b,';','];[')+']' From  @Table1 For XML Raw)
Select @Str = Replace(@Str,'['+cast(X as varchar(25))+']',Z) From  @Table2
Select @XML = @Str

Select a = r.value('@a','varchar(100)')
      ,z = r.value('@z','varchar(100)')
From  @XML.nodes('/row') as A(r)

Returns

a       z
Test    Sun
Test1   Sun;Wed
Test2   Sat
Test3   Fri;Mon
Test4   Sun;Mon;Sat
Sign up to request clarification or add additional context in comments.

Comments

1

You really should not store multiple values in the same column, it will only lead to poor performance whenever you actually have to do something with those values.


Using a CSV Splitter table valued function by Jeff Moden and the using the stuff() with select ... for xml path ('') method of string concatenation. :

select
    t1.a
  , z = stuff((
      select ';'+t2.Z
      from t1 i
        cross apply dbo.delimitedsplit8K(i.b,';') s
        inner join t2 
          on s.Item = t2.x
      where i.a = t1.a
      order by s.ItemNumber
      for xml path(''),type).value('(./text())[1]','nvarchar(max)')
    ,1,1,'')
from t1

rextester demo:

returns: http://rextester.com/HNNP95095

+-------+-------------+
|   a   |      z      |
+-------+-------------+
| Test  | Sun         |
| Test1 | Sun;Wed     |
| Test2 | Sat         |
| Test3 | Fri;Mon     |
| Test4 | Sun;Mon;Sat |
+-------+-------------+

splitting strings reference:


The function by Jeff Moden used for the demo:

create function [dbo].[delimitedsplit8K] (
      @pstring varchar(8000)
    , @pdelimiter char(1)
  )
returns table with schemabinding as
 return
  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
  )
  , e2(N) as (select 1 from e1 a, e1 b)
  , e4(N) as (select 1 from e2 a, e2 b)
  , ctetally(N) as (
    select top (isnull(datalength(@pstring),0)) 
      row_number() over (order by (select null)) from e4
  )
  , ctestart(N1) as (
    select 1 union all
    select t.N+1 from ctetally t where substring(@pstring,t.N,1) = @pdelimiter
  )
  , ctelen(N1,L1) as (
    select s.N1,
      isnull(nullif(charindex(@pdelimiter,@pstring,s.N1),0)-s.N1,8000)
    from ctestart s
  )
 select itemnumber = row_number() over(order by l.N1)
      , item       = substring(@pstring, l.N1, l.L1)
   from ctelen l
;
go

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.