-3

I want to show the below string:

Apple,Banana,Cashew,Doughnut,Elephant,Fish

into this format:

Apple, Banana + 4 others

I only want to show first two objects and rest the count !

spruless
  • 21
  • 3
  • 1
    Sql server is not great at string manipulation like this. Where does that string come from? – Sean Lange Apr 26 '22 at 13:22
  • where do these values come from ? Are they in one column in a table, or do you need to concatenate some rows/columns ? – GuidoG Apr 26 '22 at 13:28
  • Is each comma delimited value a row, or are you (foolishly) storing delimited data in your RDBMS? – Thom A Apr 26 '22 at 13:40

4 Answers4

1

This, like others have said, isn't a job to SQL Server, but your presentation layer. Based on your expected results, I assume that your commas mean you are storing delimited data in your database; a fatal flaw. If you normalised your design, you could likely easily achieve this in your application layer.

As you are using denormalised data, then you need to first normalise it, then reaggregate it too. I use an arbitrary TOP (the rows could be different every time you run the query), as the ordinal parameter is only (currently) available in Azure SQL Database; hopeful the ordinal parameter will be in SQL Server 2022.

Anyway, this works, but again, fix your design, and do this in the application layer. If you aren't on SQL Server 2017, then that is a must, not a very strong suggestion.

DECLARE @Values int = 2; --parameterised, but you could hard code

WITH Split AS(
    SELECT SS.[value],
           COUNT(*) OVER () AS [Rows]
    FROM (VALUES('Apple,Banana,Cashew,Doughnut,Elephant,Fish'))V(YourDenormalisedData)
         CROSS APPLY STRING_SPLIT(V.YourDenormalisedData,',') SS),
ArbitraryTop AS(
    SELECT TOP (@Values)
           value,
           [Rows]
    FROM Split)
SELECT STRING_AGG(value,', ') +
       CASE WHEN MAX([Rows]) > @Values THEN CONCAT(' + ',MAX([Rows])-@Values,' others') ELSE '' END
FROM ArbitraryTop;
Thom A
  • 88,727
  • 11
  • 45
  • 75
  • 'STRING_AGG' is not a recognized built-in function name. Can you please suggest! – spruless Apr 27 '22 at 05:33
  • You'll need to use the old `FOR XML PATH` method, @spruless . You didn't mention you weren't on a fully supported version of SQL Server, so I assumed you were on one. – Thom A Apr 27 '22 at 06:37
  • Hi Larnu, actually I'm using MS SQL 18. I didn't get any tags for mssql – spruless Apr 27 '22 at 13:08
  • *"MS SQL 18"* There is no such thing @spruless . It's not called "MSSQL" it's [Microsoft] SQL Server, and the latest version of SQL Server is version 15; aka SQL Server 2019. SQL Server 18 isn't expected until late this decade. – Thom A Apr 27 '22 at 13:10
  • Sorry for the typo I meant to say SQL 19* or SQL Server 2019 – spruless Apr 27 '22 at 13:13
  • Then the above will work, @spruless . `STRING_AGG` was added in SQL Server 2017. As I mentioned, if you are getting the error *"'STRING_AGG' is not a recognized built-in function name"* then you are using a version of SQL Server that is no longer in Mainstream support (2016 or prior). – Thom A Apr 27 '22 at 13:13
1

This, like others have said, isn't a job to SQL Server, but your presentation layer

If you must do it in sql, then maybe you can do it like this.
I do assume you have these values in multiple rows and need to concatenate them by some grouping, if not then make that more clear in your question

select o.id,
       ( select top 2 string_agg(o2.name, ', ')
         from   ( select top 2 o2.name, 
                         o2.id 
                  from   object o2 
                  where  o2.id = o.id
                ) o2
         where  o2.id = o.id
       ) + ' and ' + convert(varchar(50), count(o.name) - 2) + ' others'
from   object o
group by o.id

Look at this DBFiddle here to see it working

The result looks like this

id (No column name)
1 Apple, Banana and 4 others
2 Peer, Catfish and 0 others
3 Dog, Whale and 1 others

If you don't want to show the 'and x others' you can alter the query like this

select o.id,
       ( select top 2 string_agg(o2.name, ', ')
         from   ( select top 2 o2.name, o2.id 
                  from   object o2 
                  where  o2.id = o.id
                ) o2
         where  o2.id = o.id
       ) + case when count(o.name) > 2 then ' and ' + convert(varchar(50), count(o.name) - 2) + ' others'
                else ''
           end     
from   object o
group by o.id
id (No column name)
1 Apple, Banana and 4 others
2 Peer, Catfish
3 Dog, Whale and 1 others

EDIT for sql server that does not support string_agg

In case you have an older version of sql server that does not supports the string_agg function, you can do it with XML PATH to concatinate the values and stuff to remove the extra ,

select o.id,
       stuff(( select top 2 ', ' + o2.name
               from   ( select top 2 o2.name, 
                               o2.id 
                        from   object o2 
                        where  o2.id = o.id
                      ) o2
               where  o2.id = o.id
               for XML PATH('')
            ), 1, 2, ''  
       ) + case when count(o.name) > 2 then ' and ' + convert(varchar(50), count(o.name) - 2) + ' others'
                else ''
           end     
from   object o
group by o.id

DBFiddle using XML Path

The outcome will be the same again

GuidoG
  • 11,359
  • 6
  • 44
  • 79
0

I have created this, and it works for me. However, it needs optimising.

Declare @test_count int = 0, @test1_pos int = 0, @test2_pos int = 0, @test_product varchar(500), @test_product2 varchar(500),@tests varchar(500); 
--select distinct top 1 product,CHARINDEX(',',product,1),  CHARINDEX(',',product,CHARINDEX(',',product,1)+1) from ReportDB..wo_result_detail  where CustomerId=@LAB_CODE 

select distinct top 1 @test_product = product,@test1_pos=CHARINDEX(',',product,1), @test1_pos = CHARINDEX(',',product,CHARINDEX(',',product,1)+1)
from [table name]  where [condition] 

select distinct top 1 @test_product = product, @test_product2 =
case when @test1_pos <> 0 or  @test2_pos <> 0  then ( 
                select top 1 left(product,CHARINDEX(',',product,CHARINDEX(',',product,1)+1)-1) from [table name]  where [condition] 
                )
                else @test_product
                end from [table name]  where [condition] 

select @test_count = (len(replace(@test_product,',',', '))+1) - len(@test_product)

select top 1 @tests= case when @test_count> 2 then  concat ( @test_product2 ,' + ',@test_count-2 ,' Others') 
else @test_product end
halfer
  • 19,824
  • 17
  • 99
  • 186
spruless
  • 21
  • 3
-1

This task can be solved with the power of the cursor, by dividing the line by the delimenter using function split_string.

  --@string - our input string
DECLARE @string NVARCHAR(MAX) = 'Apple,Banana,Cashew,Doughnut,Elephant,Fish';

--@count - the number of words in @string
DECLARE @count INT = 0;

--@countrestwords - count of rest words
DECLARE @countrestwords INT = 0;

--@resultstring - result string
DECLARE @resultstring NVARCHAR(MAX) = '';

        DECLARE stringcursor CURSOR FOR 
        SELECT 
            VALUE 
        FROM string_split(@string,',')

        OPEN stringcursor 

        FETCH FROM stringcursor INTO @string
        WHILE @@FETCH_STATUS = 0
            BEGIN
                IF @count = 0
                    BEGIN
                        SET @resultstring = @string;

                    END
                ELSE IF @count = 1
                    BEGIN
                        SET @resultstring = @resultstring +',' +@string ;
            
                    END
                ELSE 
                    BEGIN
                        SET @resultstring = @resultstring;
                        SET @countrestwords = @countrestwords + 1;

                        --SELECT @countrestwords
                    END


                SET @count = @count + 1;


                FETCH NEXT FROM stringcursor INTO @string

            END

        CLOSE stringcursor
        DEALLOCATE stringcursor

SELECT @resultstring + ' + ' + CONVERT(NVARCHAR(MAX),@countrestwords)+' others' ;

GO
sahb
  • 1
  • 4
  • Hii sahb, @count_restwords the count is 1 greater than expected Expected : Apple, Banana+ 4 Current : Apple, Banana+ 5 – spruless Apr 27 '22 at 05:31
  • 1
    Note that using a cursor is the slowest possible solution, cursors should only be used when there really is no other way to do it. And when you find yourself in that situation then the most logical conclusion would be to not do it in sql but in the client software. – GuidoG Apr 27 '22 at 05:56
  • 1
    And honestly, if you already used the split_string function to separate the values, then why do you still need a cursor ? The split_string provides you with a nice set of results where you can do queries on, why loop it again ? – GuidoG Apr 27 '22 at 06:00
  • Hi Yes . I checked. Right . Displays incorrectly. I'm working on a solution. I'll provide a fix soon. @spruless, thank you. – sahb Apr 27 '22 at 06:06
  • @GuidoG, It made it easier for me to solve the problem. As for performance, I did not know, I will take it in consideration in the future. Thanks. – sahb Apr 27 '22 at 06:17
  • I have updated the problem solution. Now it is displayed correctly as in the question. – sahb Apr 27 '22 at 06:39