Для цикла в SQL, чтобы найти 1 позицию в последовательности 0 и 1 - PullRequest
0 голосов
/ 31 октября 2018

Ниже приведен цикл for в c #, который возвращает позицию 1 в последовательности 0 и 1, вот мой код

public string OnesPosition(string statusBits)
{
     string onePos = "";
     for (int i = 0; i < statusBits.Length; i++)
         {
           if (statusBits[i] == '1')
           {
              onePos = onePos + Convert.ToSingle(i + 1) + ",";
           }
         }
    onePos = string.IsNullOrEmpty(onePos ) ? "0," : onePos ;
    return onePos;
}

var result = OnesPosition("00000000000101");

Это вернет: результат = 12,14

Как я могу сделать это в SQL-запросе или с помощью функции SQL? Использование SQL Server Management Studio v17.9

Ответы [ 7 ]

0 голосов
/ 31 октября 2018

Решение:

Другой возможный подход - использование рекурсивного CTE (который возвращает каждую цифру и цифру) и групповой конкатенации.

Использование STRING_AGG () (из SQL Server 2017):

DECLARE @ones varchar(16)
SET @ones = '1000000001001011';

WITH Digits AS (
   SELECT 1 AS DigitPosition, SUBSTRING(@ones, 1, 1) AS Digit
   UNION ALL
   SELECT DigitPosition + 1, SUBSTRING(@ones, DigitPosition + 1, 1)
   FROM Digits
   WHERE DigitPosition < LEN(@ones)
) 
SELECT STRING_AGG(DigitPosition, ',')
FROM Digits
WHERE Digit = '1'

Использование ДЛЯ XML :

DECLARE @ones varchar(16)
SET @ones = '1000000001001011';

WITH Digits AS (
   SELECT 1 AS DigitPosition, SUBSTRING(@ones, 1, 1) AS Digit
   UNION ALL
   SELECT DigitPosition + 1, SUBSTRING(@ones, DigitPosition + 1, 1)
   FROM Digits
   WHERE DigitPosition < LEN(@ones)
) 
SELECT CONVERT(varchar(max), DigitPosition) + ','
FROM Digits
WHERE Digit = '1'
FOR XML PATH('')

Выход:

1,10,13,15,16
0 голосов
/ 01 ноября 2018

или это ...

DECLARE @string VARCHAR(100) = '00000000000101';

WITH 
    cte_n1 (n) AS (SELECT 1 FROM (VALUES (1),(1),(1),(1),(1),(1),(1),(1),(1),(1)) n (n)), 
    cte_n2 (n) AS (SELECT 1 FROM cte_n1 a CROSS JOIN cte_n1 b),
    cte_Tally (n) AS (
        SELECT TOP (LEN(@string))
            ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
        FROM
            cte_n2 a CROSS JOIN cte_n2 b
        )
SELECT 
    STRING_AGG(t.n, ',')
FROM
    cte_Tally t
WHERE 
    SUBSTRING(@string, t.n, 1) = '1';
0 голосов
/ 31 октября 2018

Вот функция, основанная на ответе @scsimon, и она сработала.

CREATE FUNCTION [dbo].[ConvertTo1Positions]  
(  
    @ones AS varchar(16),  
    @pos AS INT = 1,
    @result varchar(256) 
) RETURNS VARCHAR(MAX) AS BEGIN  

   while @pos <= len(@ones)
begin
    set @result = @result + iif(substring(@ones,@pos,1) = 1,cast(@pos as char),'')
    set @pos = @pos + 1
end 
    RETURN @result;  

END  
0 голосов
/ 31 октября 2018

Еще один метод, использующий таблицу подсчета и просто прямой tsql:

WITH Tally(i) AS (
   SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS i
   FROM (VALUES (0), (0), (0), (0), (0), (0), (0)) a(n)
   CROSS JOIN (VALUES (0), (0), (0), (0), (0), (0)) b(n)
) 
SELECT bitloc
FROM
    (
        SELECT SUBSTRING(x.d, i, 1)  as bitset, i bitloc
        FROM (VALUES ('00000000000101')) x(d)
        CROSS JOIN Tally
    ) sub
WHERE bitset = 1
0 голосов
/ 31 октября 2018

Я не думаю, что это хорошая идея - использовать цикл, но так как вы этого хотите

DECLARE @Str VARCHAR(45) = '000001000000101',
        @OutPut VARCHAR(45) = '',
        @I INT = 1;

WHILE @I <= LEN(@Str)
BEGIN
 IF (SELECT SUBSTRING(@Str, @I, 1)) = '1'
   SET @OutPut = @OutPut + (SELECT CAST(@I AS VARCHAR(10))) + ','; 
   --Convert.ToSingle(i + 1) why +1?
 SET @I = @I + 1;
END


SELECT @OutPut;
0 голосов
/ 31 октября 2018

Просто еще один вариант

* ** 1003 тысяча два * Пример
Declare @S varchar(50) = '00000000000101'

Select Stuff((Select concat(',',N )
                From ( 
                      Select Top (len(@S)) N=Row_Number() Over (Order By (Select NULL)) 
                       From  master..spt_values 
                      ) s 
                Where substring(@S,N,1)='1'
                Order By N
                For XML Path ('')),1,1,'')

Returns

12,14
0 голосов
/ 31 октября 2018

Циклы не очень хорошо работают в SQL Server, но вот метод.

declare @ones varchar(16) = '00000000100101'
declare @pos int = 1
declare @result varchar(256) = ''

while @pos <= len(@ones)
begin
    set @result = @result + case when substring(@ones,@pos,1) = 1 then ',' + cast(@pos as varchar) else '' end
    set @pos = @pos + 1
end

select right(@result,len(@result) - 1)
...