Количество 0 между 1-мя — SQL

#sql-server #string #tsql #count

#sql-сервер #строка #tsql #количество

Вопрос:

Мне нужен запрос или функция для подсчета 0 между 1 в строке.

Например:

 String1 = '10101101' -> Result=3
String2 = '11111001101' -> Result=1
String3 = '01111111111' -> Result=1
  

Мне нужно искать только шаблон 101 или шаблон 01, если он находится в начале строки.

Комментарии:

1. Можем ли мы спросить, какую проблему вы пытаетесь решить здесь? Это просто головоломка?

2. Если строка начинается с, 01 то 0 на самом деле это не «между» 1 .

3. какова максимальная длина строки?

4. Честно говоря, кажется, что это было бы лучше с чем-то, что поддерживает сопоставление шаблонов РЕГУЛЯРНЫХ выражений.

Ответ №1:

Вы можете попытаться разложить входные строки с помощью SUBTRING() и таблицы чисел:

 SELECT
   String, COUNT(*) AS [101Count]
FROM (
   SELECT
      v.String,
      SUBSTRING(v.String, t.No - 1, 1) AS PreviousChar,
      SUBSTRING(v.String, t.No, 1) AS CurrentChar,
      SUBSTRING(v.String, t.No   1, 1) AS NextChar
   FROM (VALUES
      ('10101101'),
      ('11111001101'),
      ('01111111111')
   ) v (String)
   CROSS APPLY (VALUES (1), (2), (3), (4), (5), (6), (7), (8), (9), (10)) t (No)
) cte
WHERE
   CASE WHEN PreviousChar = '' THEN '1' ELSE PreviousChar END = '1' AND
   CurrentChar = '0' AND
   NextChar = '1'
GROUP BY String
  

Результат:

 String      101Count
10101101    3
11111001101 1
01111111111 1
  

Примечания:

Таблица с псевдонимом v является исходной таблицей, таблица с псевдонимом t является таблицей чисел. Если входные строки содержат более 10 символов, используйте соответствующую таблицу чисел (tally).

Ответ №2:

— Это преобразует «111101101010111» в «01101010» и «011101000» в «01110»

 regexp_replace(field, '^1*(.*)1*0*$', '1')
  

— Это преобразует «01101010» в «0000»

 regexp_replace(field, '1', '')
  

— Это подсчитывает длину строки, возвращая 4 для ‘0000’:

 LENGTH(field)
  

— Собрать все вместе:

 LENGTH(
    regexp_replace(
        regexp_replace(field, '^1*(.*)1*0*$', '1')
    , '1', '')
)
  

Различные или более сложные случаи требуют модификации регулярного выражения.

Обновить

Теперь я вижу, что под «нулями между 1 с» вы имеете в виду последовательности «101». Это сложнее из-за возможности наличия «10101». Предположим, вы хотите посчитать это как 2:

  • замените 101 на 11011. Теперь 10101 станет либо 1101101, либо 1101111011. В любом случае у вас есть последовательность «101», которая находится далеко друг от друга, и все еще есть только две из них.

  • замените все 101 с на ‘X’. Теперь у вас есть 1X11X1

  • замените [01] пустой строкой. Теперь у вас есть XX.

  • используйте длину для подсчета X.

Любую дополнительную специальную последовательность, такую как «01» в начале, вы можете преобразовать в первую очередь с помощью «X1» («10» в конце станет «1X»), которая затем аккуратно сложится обратно в описанном выше рабочем процессе.

Ответ №3:

Используя LIKE оператор с % , вы можете решить, как искать конкретную строку. В этом SQL-запросе я говорю, что хочу, чтобы каждая запись начиналась как 101 или 01 .

 SELECT ColumnsYouWant FROM TableYouWant
WHERE ColumnYouWant LIKE '101%' OR '01%';
  

Вы можете просто COUNT ColumnYouWant , вот так:

 SELECT COUNT(ColumnYouWant) FROM TableYouWant
WHERE ColumnYouWant LIKE '101%' OR '01%';
  

Или вы можете использовать метод вашего внутреннего языка для count результатов, возвращаемых первым запросом. Этот count метод будет зависеть от языка, с которым вы работаете.

Документация по SQL для LIKE :https://www.w3schools.com/sql/sql_like.asp

Документация по SQL для COUNT ; https://www.w3schools.com/sql/sql_count_avg_sum.asp

Комментарии:

1. Как это «подсчитывает» экземпляры шаблонов в строке?

Ответ №4:

Другие решения не учитывают все символы (максимум 11, из показанных примеров)

Данные

 drop table if exists #tTEST;
go
select * INTO #tTEST from (values 
(1, '10101101'),
(2, '11111001101'),
(3, '01111111111')) V(id, string);

  

Запрос

 ;with
split_cte as (
    select id, n, substring(t.string, v.n, 1) subchar
    from #tTEST t
         cross apply (values (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),
                             (11),(12),(13),(14),(15),(16),(17),(18),(19),(20)) v(n)
    where v.n<=len(t.string)),
lead_lag_cte as (
    select id, n, lead(subchar, 1, 9) over (partition by id order by n) lead_c, subchar, 
           lag(subchar, 1, 9) over (partition by id order by n) lag_c
    from split_cte) 
select id, sum(case when (lead_c=1 and lag_c=9) then 1 else
                case when (lead_c=1 and lag_c=1) then 1 else 0 end end) zero_count
from lead_lag_cte
where subchar=0
group by id;
  

Результаты

 id  zero_count
1   3
2   1
3   1
  

Ответ №5:

Другой способ, возможно, быстрее:

 DECLARE @T TABLE (ID INT, STRING VARCHAR(32));
INSERT INTO @T 
VALUES (1, '10101101'),
       (2, '11111001101'),
       (3, '01111111111');

SELECT *, LEN(STRING) - LEN(REPLACE(STRING, '0', '')) AS NUMBER_OF_ZERO
FROM   @T
  

Результат:

          ID STRING                           NUMBER_OF_ZERO
----------- -------------------------------- --------------
          1 10101101                                      3
          2 11111001101                                   3
          3 01111111111                                   1
  

Ответ №6:

 select (len(replace('1' x, '101', '11011')) - len(replace(replace('1' x, '101', '11011'), '101', '')))/3
from
(
values
('10101101'),
('11111001101'),
('01111111111'),
('01010101010101010101')
) v(x);