Счетчик автоматического увеличения, если длина поля больше n

#sql #oracle #auto-increment

#sql #Oracle #автоматическое увеличение

Вопрос:

Меня попросили включить поле ComNo, и если длина поля комментариев превышает 5 символов, остальные символы в комментарии должны появиться в следующей строке, а поле ComNo должно быть увеличено на единицу

Ввод

 EmpID EmpName ServiceNumber Date         Comments
1     a      123            23-03-1990   wednesday
1     a      1234           24-04-1990   Test12
2     b      234            24-05-2016   Todayis
  

И ожидаемый результат

 EmpID EmpName ServiceNumber Date       ComNo Comments
1     a       123           23-03-1990 1     wedne
1     a       123           23-03-1990 2     sday
1     a       1234          24-04-1990 1     Test1
1     a       1234          24-04-1990 2     2
2     b       234           24-05-2016 1     Today
2     b       234           24-05-2016 2     is
  

У меня есть идея, как реализовать это с помощью процедуры plsql, но можем ли мы реализовать то же самое с помощью sql-запроса?

Ответ №1:

Вот решение, которое не делает предварительных предположений о длине комментариев. Я добавил две строки, одну короткую (менее 5 символов) и одну длиннее 10 символов, а также одну с комментарием NULL, чтобы убедиться, что строка не потеряна, чтобы тщательно протестировать решение.

Я предполагаю (empid, dt) , что это уникальная комбинация (возможно, первичный ключ) в базовой таблице. Кстати, я надеюсь, что вы на самом деле не используете Date в качестве имени столбца в своей схеме.

Решение не содержит предложения WITH; оно начинается с select empid... предложения ORDER BY, которое также может не понадобиться.

 with
     test_data ( empid, empname, servicenumber, dt, comments ) as (
       select 1, 'a',  123, to_date('23-03-1990', 'dd-mm-yyyy'), 'wednesday'   from dual union all
       select 1, 'a', 1234, to_date('24-04-1990', 'dd-mm-yyyy'), 'Test12'      from dual union all
       select 2, 'b',  234, to_date('24-05-2016', 'dd-mm-yyyy'), 'Todayis'     from dual union all
       select 2, 'b',  235, to_date('25-05-2016', 'dd-mm-yyyy'), 'Joe'         from dual union all
       select 3, 'c',  238, to_date('25-05-2016', 'dd-mm-yyyy'), ''            from dual union all
       select 4, 'c', 2238, to_date('25-05-2016', 'dd-mm-yyyy'), 'longer string' from dual
     )
select empid, empname, servicenumber, dt, level as comno,
       substr(comments, 5 * level - 4, 5) as comments
from   test_data
connect by level <= 1   length(comments) / 5
     and   prior empid = empid
     and   prior dt    = dt
     and   prior sys_guid() is not null
order by empid, dt
;

     EMPID E SERVICENUMBER DT              COMNO COMMENTS
---------- - ------------- ---------- ---------- --------------------
         1 a           123 1990-03-23          1 wedne
         1 a           123 1990-03-23          2 sday
         1 a          1234 1990-04-24          1 Test1
         1 a          1234 1990-04-24          2 2
         2 b           234 2016-05-24          1 Today
         2 b           234 2016-05-24          2 is
         2 b           235 2016-05-25          1 Joe
         3 c           238 2016-05-25          1
         4 c          2238 2016-05-25          1 longe
         4 c          2238 2016-05-25          2 r str
         4 c          2238 2016-05-25          3 ing
  

Добавлено: если ваши данные имеют тип данных CLOB, лучше использовать dbms_lob версию substr . Кроме того, если вы должны разделить данные на 75 символьных сегментов, вы должны настроить несколько чисел. Вот то же самое решение, с этими двумя изменениями и без предложения ORDER BY (не требуется, если это используется для переноса данных в другой продукт БД). ПРИМЕЧАНИЕ: dbms_lob.substr() имеет ли значение «сумма» и «смещение» (второй и третий аргументы) обратные по сравнению с обычной substr() функцией; обратите внимание, что при сравнении решений.

 select empid, empname, servicenumber, dt, level as comno,
       dbms_lob.substr(comments, 75, 75 * level - 74) as comments
from   test_data
connect by level <= 1   length(comments) / 75
     and   prior empid = empid
     and   prior dt    = dt
     and   prior sys_guid() is not null
;
  

Ответ №2:

Вот один из способов сделать это:

 with n as (
      select 1 as n from dual union all
      select 2 from dual
     )
select EmpID, EmpName, ServiceNumber, Date, ComNo,
       substr(comments, n.n * 5 - 4, 5) as Comments
from t join
     n
     on length(comments) >= n.n * 5   1;
  

Примечание: в вашем примере есть только комментарии длиной до 10 символов, поэтому нужны только значения 1 и 2 n . Вы можете создавать дополнительные строки, расширяя n .

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

1. Привет, Гордон, спасибо за ваш ответ. В моем примере комментарии содержат только до 10 символов, но фактические данные содержат комментарии длиной более 4000 символов.

2. @AnnieJeba . , , Это решение легко расширяется на всякий случай, если это так. Однако я отмечаю, что Oracle ограничивает строки до 4000 символов, поэтому я был бы удивлен, если они длиннее этого.

3. @GordonLinoff — Документация Oracle по типу данных CLOB: docs.oracle.com/cd/B28359_01/server.111/b28318 /…

4. @AnnieJeba — если ваши данные переполнены, вам может быть полезно использовать пакет dbms_lob («lob-версии» substr и instr ). docs.oracle.com/cd/B28359_01/appdev.111/b28419 /… Однако вопрос: если комментарии могут состоять из тысяч символов, зачем разбивать их на фрагменты из пяти символов? Почему пять, а не, например, 80?

5. Фактическое требование состоит в том, чтобы сократить комментарии до 75 символов в каждой строке, поскольку выборка данных будет огромной, которую я только что упомянул как 5 символов. Они загружают данные из Oracle в SAP DB, а ограничение длины поля в SAP составляет 75 символов, поэтому меня попросили извлечь отчет в соответствии с тем, как они загружают данные в SAP

Ответ №3:

Вариантом грубой силы было бы просто UNION объединить записи, полученные в результате разделения комментариев на 5 символов за раз:

 SELECT EmpID, EmpName, ServiceNumber, Date, 1, SUBSTR(Comments, 1, 5) AS Comments
FROM yourTable
UNION ALL
SELECT EmpID, EmpName, ServiceNumber, Date, 2, SUBSTR(Comments, 6, 5) AS Comments
FROM yourTable
WHERE SUBSTR(Comments, 6, 5) <> ''  -- but don't show a record if
UNION ALL                           -- all characters already used
...
  

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

1. @AnnieJeba Тогда взгляните на ответ Гордона. Кстати, зачем вам это нужно?

2. docs.oracle.com/cd/E11882_01/server.112/e41084/…

3. @mathguy С какой стати я думал, что OP использует Postgres?