Split/explode comma delimited string with Sybase S

2019-02-07 15:10发布

问题:

This question already has an answer here:

  • How do I split a string so I can access item x? 42 answers

UPDATE: Someone marked this question as duplicate of How do I split a string so I can access item x. But it's different, my question is about Sybase SQL Anywhere, the other is about MS SQL Server. These are two different SQL engines, even if they have the same origin, they have different syntax. So it's not duplicate. I wrote in the first place in description and tags that it's all about Sybase SQL Anywhere.

I have field id_list='1234,23,56,576,1231,567,122,87876,57553,1216'

and I want to use it to search IN this field:

SELECT * 
FROM table1
WHERE id IN (id_list)
  • id is integer

  • id_list is varchar/text

But in this way this doesn't work, so I need in some way to split id_list into select query.

What solution should I use here? I'm using the T-SQL Sybase ASA 9 database (SQL Anywhere).

Way I see this, is to create own function with while loop through, and each element extract based on split by delimiter position search, then insert elements into temp table which function will return as result.

回答1:

This can be done without using dynamic SQL but you will need to create a couple of supporting objects. The fist object is a table valued function that will parse your string and return a table of integers. The second object is a stored procedure that will have a parameter where you can pass the string (id_list), parse it to a table, and then finally join it to your query.

First, create the function to parse the string:

CREATE FUNCTION [dbo].[String_To_Int_Table]
(
         @list NVARCHAR(1024)
       , @delimiter NCHAR(1) = ',' --Defaults to CSV
)
RETURNS
    @tableList TABLE(
       value INT
       )
AS

BEGIN
   DECLARE @value NVARCHAR(11)
   DECLARE @position INT

   SET @list = LTRIM(RTRIM(@list))+ ','
   SET @position = CHARINDEX(@delimiter, @list, 1)

   IF REPLACE(@list, @delimiter, '') <> ''
   BEGIN
          WHILE @position > 0
          BEGIN 
                 SET @value = LTRIM(RTRIM(LEFT(@list, @position - 1)));
                 INSERT INTO @tableList (value)
                 VALUES (cast(@value as int));
                 SET @list = RIGHT(@list, LEN(@list) - @position);
                 SET @position = CHARINDEX(@delimiter, @list, 1);

          END
   END   
   RETURN
END

Now create your stored procedure:

    CREATE PROCEDURE ParseListExample
    @id_list as nvarchar(1024)
    AS
    BEGIN

    SET NOCOUNT ON;

    --create a temp table to hold the list of ids
    CREATE TABLE #idTable (ID INT);

    -- use the table valued function to parse the ids into a table.
    INSERT INTO #idTable(ID)
    SELECT Value FROM   dbo.String_to_int_table(@id_list, ',');

    -- join the temp table of ids to the table you want to query...
    SELECT T1.* 
    FROM table1 T1
    JOIN #idTable T2
    on T1.ID = T2.ID

Execution Example:

 exec ParseListExample @id_list='1234,23,56,576,1231,567,122,87876,57553,1216'

I hope this helps...



回答2:

You use text in your query and this is not going to work. Use dynamic query.



回答3:

Like Mikael Eriksson said, there is answer at dba.stackexchange.com with two very good solutions, first with use of sa_split_list system procedure, and second slower with CAST statement.

For the Sybase SQL Anywhere 9 sa_split_list system procedure not exist, so I have made sa_split_list system procedure replacement (I used parts of the code from bsivel answer):

CREATE PROCEDURE str_split_list
(in str long varchar, in delim char(10) default ',')
RESULT(
  line_num integer,
  row_value long varchar)
BEGIN
  DECLARE str2 long varchar;
  DECLARE position integer;

   CREATE TABLE #str_split_list (
   line_num integer DEFAULT AUTOINCREMENT,
   row_value long varchar null,
   primary key(line_num));

   SET str = TRIM(str) || delim;
   SET position = CHARINDEX(delim, str);

   separaterows:
   WHILE position > 0 loop
       SET str2 = TRIM(LEFT(str, position - 1));
       INSERT INTO #str_split_list (row_value)
       VALUES (str2);
       SET str = RIGHT(str, LENGTH(str) - position);
       SET position = CHARINDEX(delim, str);
    end loop separaterows;

   select * from #str_split_list order by line_num asc;

END

Execute the same way as sa_split_list with default delimiter ,:

select * from str_split_list('1234,23,56,576,1231,567,122,87876,57553,1216')

or with specified delimiter which can be changed:

select * from str_split_list('1234,23,56,576,1231,567,122,87876,57553,1216', ',')


回答4:

Good contribution from bsivel answer, but to generalise it (for other separators than a comma), then the line

SET @list = LTRIM(RTRIM(@list))+ ','

must become

SET @list = LTRIM(RTRIM(@list))+ @delimiter

The first version will only work for comma-separated lists.



回答5:

The dynamic query approach would look like this:

create procedure ShowData @IdList VarChar(255)
as
  exec ('use yourDatabase; select * from MyTable where Id in ('+@IdList+')')