-1

I am working in an existing stored procedure. In that, they fetch some data using select Queries. Now I am trying to fetch some more data. The problems I have is, the column I am using to fetch data from another table have more than one Id saved in with comma(,) in between.

Procedure with 2 tables as Tbl_av,Tbl_aa.

I have one column in Tbl_av as processId that is in Varchar and it stores data like 1,2,4 etc.. (that is, it stores the Id of the Process).

Now I am trying to get the Process Name from one more table Tbl_Process using this ProcessId under the table Tbl_av. In that table, the process Id is Unique one and it is in INTEGER.

Tbl_Process as follows:

enter image description here

Tbl_av as follows:

enter image description here

I have procedure few part as follows:

SET @strQuery= 'SELECT isnull(av.ID, 0) AS ID
      ,isnull(ItemNumber, '''') AS Number  
      ,isnull(av.ItemName,'''') as Item Name
      ,av.Description
      ,(select top 1 Name from TBL_Names where Id =aa.id)as Person Incharge from Tbl_AV av, Tbl_aa aa WHERE av.S_number = aa.S_number'

Now what I am trying to do is , I need to fetch that Process names from Tbl_Process using this Tbl_av ProcessId's inside the procedure.

I dont know how to achieve it, as it both columns are in different datatype and more than 1 id is saved in one column

NOTE: We can simply achieve this as 'SELECT Process_Name from Tbl_Process WHERE Id in (av.ProcessId)' - But doing this ,displays data in column format.. I want data to be selected as if Id is 1,2 means I want my output as Process_Name = Item 1,Item 2.

Kindly help.

Riya
  • 157
  • 5
  • 22
  • 2
    This is a denormalized database model that doesn't reach First Normal Form. You shouldn't expect to perform joins against a table like this one. If you need joins I would strongly suggest you normalize the design at least to 3NF. – The Impaler Feb 04 '22 at 16:19
  • What version of SQL Server are you running? – Chad Baldwin Feb 04 '22 at 16:42
  • Does this answer your question? [Joining a table based on comma separated values](https://stackoverflow.com/questions/26236436/joining-a-table-based-on-comma-separated-values) – Stephen Jennings Feb 04 '22 at 16:48
  • @StephenJennings No that question doesnt work for me – Riya Feb 04 '22 at 17:33
  • @ChadBaldwin SQL 2014 – Riya Feb 04 '22 at 17:33
  • @TheImpaler I cannot modify the design as it is an existing procedure and many pages running based on this SP..thats Y I am confused how to fix it.. Between, I am new to some deep SQL concepts too.. – Riya Feb 04 '22 at 17:35
  • 1
    Stop using [old-style joins](https://sqlblog.org/2009/10/08/bad-habits-to-kick-using-old-style-joins). You have picked up a lot of bad habits - time to invest in learning good ones. In addition, splitting strings is THE MOST COMMON question in SO. There are many resources you can find that contain code usable for any version of sql server. – SMor Feb 04 '22 at 18:16

1 Answers1

1

Two tricks are needed. One is joining based on a comma-separated list of IDs. That can easily be done poorly resulting in unwanted matches such as 1 and 2 matching 12. The article Stephen Jennings referenced has some good reliable solutions.

The second is concatenating a collection of results into a single string. For recent versions of SQL Server, the STRING_AGG is the preferred solution. For older versions (such as 2014) the most common method is the "FOR XML" trick.

I've combined the two techniques below.

DECLARE @Tbl_Process TABLE(ID INT, process_Name VARCHAR(100))
INSERT @Tbl_Process
VALUES (1, 'Item 1'), (2, 'Item 2'), (3, 'Item 3'), (4, 'Item 4'), (5, 'Item 5'), (12, 'Item 12')

DECLARE @Tbl_av TABLE(ID INT, ProcessId VARCHAR(100))
INSERT @Tbl_av
VALUES (1, '1,3,5'), (2, '2,4'), (3, '1,2,3'), (4, '1,5'), (5, ''), (6, '3,4,12')

SELECT AV.*, PL.*
FROM @Tbl_av AV
CROSS APPLY (
    SELECT ISNULL(STUFF(
        (
            SELECT ',' + P.process_Name
            FROM @Tbl_Process P
            --(bad) WHERE CHARINDEX(CONVERT(VARCHAR, P.ID), AV.ProcessId) > 0
            WHERE ',' + AV.ProcessId + ',' LIKE '%,' + CONVERT(VARCHAR, P.ID) + ',%'
            ORDER BY P.ID -- alternately ORDER BY P.process_Name
            FOR XML PATH(''), TYPE
        ).value('text()[1]','nvarchar(max)')
    , 1, 1, '')
    , '(none)')
    AS Process_Names
) PL

Results

ID ProcessId Process_Names
1 1,3,5 Item 1,Item 3,Item 5
2 2,4 Item 2,Item 4
3 1,2,3 Item 1,Item 2,Item 3
4 1,5 Item 1,Item 5
5 (none)
6 3,4,12 Item 3,Item 4,Item 12

The FOR XML PATH('') trick causes the results to concatenate into a single XML string with no tags. The , TYPE together with .value('text()[1]','nvarchar(max)') safely extracts the resulting text, undoing any XML specific text encodings such as &, <, or >. The STUFF() removes the leading comma, and the ISNULL() provides a default if there are no values.

See How Stuff and 'For Xml Path' work in SQL Server? for more on how the FOR XML trick works.

If you prefer comma-space list separators, you can update the ',', but will also need to adjust the STUFF to strip two characters instead of one.

The contents of the cross apply could be moved directly into the SELECT clause, but as a matter of style, the CROSS APPLY allows separation of complex logic from the rest of the query.

T N
  • 4,322
  • 1
  • 5
  • 18
  • why not use CHARINDEX ? – jjdesign Feb 04 '22 at 18:51
  • @jjdesign You can, but you have to be careful to wrap both the search term and search target in extra delimiters to avoid false hits caused by partial matches. The following woul dwork in this case: `WHERE CHARINDEX(',' + CONVERT(VARCHAR, P.ID) + ',', ',' + AV.ProcessId + ',') > 0`. The LIKE version is just a bit shorter and is likely optimized under the covers to be effectively the same as CHARINDEX. (That was also the form I pulled from the referenced join article.) – T N Feb 04 '22 at 19:08
  • Correct syntax (to unescape XML symbols correctly) is `for XML PATH(''), TYPE ).value('text()[1]','nvarchar(max)')` See the answer in [this question](https://stackoverflow.com/questions/70365793/delete-symbol-at-string-end-sql/70375041#70375041) – GuidoG Feb 05 '22 at 10:26
  • @TN hi , u declared and inserted values both mapping field as varchar, but mine is 2 different table already having record one with int and another 1 with varchar.. Is that ok ? and for me to upgrading SQL,what are the main topics to learn? like stuff etc..? – Riya Feb 05 '22 at 17:25
  • @TN its working..Thank u so much.. but I dont understand the concept clearly.. I copy pasted ur codes and changed field name as my required one.. I want to learn this basics.. Plz guide me the proper link to learn these things – Riya Feb 05 '22 at 17:36
  • @Riya - Which aspects specifically do you have questions on? The LIKE expression? the FOR XML construct? the stuff/replace/isnull functions? The CROSS APPLY? Did you read the article that I linked above? – T N Feb 05 '22 at 18:23
  • @TN for the xml and cross apply.. I ll check that link – Riya Feb 07 '22 at 04:36
  • @GuidoG. Thanks for the pointer. I've updated my answer to use `TYPE` and `.value()`. – T N Feb 07 '22 at 15:24
  • No problem, it is so much easier than using all the replace statements, I also learned it from SO in an answer similar to yours – GuidoG Feb 07 '22 at 16:02