Каков хороший способ назначить номера заказов упорядоченным строкам в таблице в Sybase

У меня есть таблица T (структура ниже), которая изначально содержит все значения NULL в целочисленном столбце order:

col1  varchar(30),
col2  varchar(30),
order int NULL

У меня также есть способ упорядочить столбцы «colN», например.

SELECT * FROM T ORDER BY some_expression_involving_col1_and_col2

Как лучше всего присвоить - В SQL - числовые значения порядка 1-N таблице порядка, чтобы значения порядка соответствовали порядку строк, возвращаемому приведенным выше ORDER BY?

Другими словами, мне нужен один запрос (синтаксис Sybase SQL, поэтому нет подсчета строк Oracle), который присваивает значения order, чтобы SELECT * FROM T ORDER BY order возвращал 100% тот же порядок строк, что и запрос выше.

Запрос НЕ обязательно должен обновлять таблицу T на месте, я согласен с созданием копии таблицы T2, если это упростит запрос.

ПРИМЕЧАНИЕ 1. Решение должно быть реальным запросом или набором запросов, не включающим цикл или курсор.

ПРИМЕЧАНИЕ 2: Предположим, что данные однозначно упорядочиваются в соответствии с порядком, указанным выше, - не нужно беспокоиться о ситуации, когда 2 строки могут быть назначены в одном и том же порядке случайным образом.

ПРИМЕЧАНИЕ 3. Я бы предпочел общее решение, но если вам нужен конкретный пример выражения заказа, скажем:

SELECT * FROM T 
ORDER BY CASE WHEN col1="" THEN "AAAAAA" ELSE col1 END, ISNULL(col2, "ZZZ")

person DVK    schedule 12.05.2010    source источник


Ответы (3)


Я использовал в качестве примера стол ройшед в pubs2. Я добавил столбец id (int NULL) и использовал в качестве логики упорядочения следующее:

substring(title_id,3,4) + right(replicate('0',5) + convert(varchar,lorange),5)

Затем я создал (и обновил) представление. Чтобы избежать дублирования, я использовал строки

Declare @strSql varchar(1000), @strOrder varchar(256)
set @strOrder = "substring(title_id,3,4) + right(replicate('0',5) + convert(varchar,lorange),5)"

set @strSql = "create view my_roysched (id,my_order)
as 
select id, (select count(*) from (select " + @strOrder + " as my_order from roysched) BETA 
              where BETA.my_order <= ALPHA.my_order) as my_id 
      from (select id," + @strOrder + " as my_order from roysched ) as ALPHA 

update my_roysched 
set id = my_order

drop view my_roysched"

exec (@strSql)

Теперь, если вы дадите

select * from roysched
order by substring(title_id,3,4) + right(replicate('0',5) + convert(varchar,lorange),5)

вы увидите, что идентификатор соответствует порядку строк, возвращаемых указанным выше порядком,

person George Dontas    schedule 13.05.2010
comment
Аккуратный! Я не на 100% могу использовать это (мне не нужно оценивать производительность при многократном выполнении этого), но тем не менее технически это впечатляет! +1 - person DVK; 13.05.2010

ДВК

Люди ответили на ваш конкретный вопрос, и вы выбрали ответ.

Но у всей проблемы есть более глубокая проблема.

  1. Насколько я понимаю, Order - это столбец в таблице; который определяет Порядок, в котором должны быть представлены строки (или аналогичный). Это правильно ?

  2. Если это так, то таблица сильно денормализована: вы нарушили 3NF (правило функциональной зависимости нарушено); у вас есть производное значение в столбце (дублированная информация); введены аномалии обновления (когда вы обновляете данные в столбце, теперь вам нужно обновить дублированные [зависимые] данные в другом столбце).

  3. Самым важным из них, конечно же, являются аномалии обновления. Вы больше не можете вставить строку в таблицу (теперь нереляционную, ненормализованную). Для каждой вставки необходимо пересчитать и ОБНОВИТЬ столбец Order по крайней мере для одной и максимум для каждой второй строки в таблице.

  4. Ничего из этого не нужно.

    • Я мог бы ответить на ваш вопрос только в рамках вашего вопроса, чтобы заполнить столбец Order (который будет вызываться сегментом кода, который постоянно его поддерживает). Требуется код 5 строк. Чистый стандартный SQL на базовой таблице. Нет просмотров. Нет временных таблиц. Никаких циклов или курсоров. Работает так же, как функция Oracle RANK(), и поэтому является универсальной (просто подставьте имена таблиц и столбцов).

    • но дело в том, что это тот же код, который мог бы и будет использоваться во время выполнения для создания столбца Order, если бы это было производное значение, а не реальный столбец в базе данных. Таким образом, столбец Order и весь код, необходимый для его обслуживания, можно удалить.

    • поэтому я также могу дать вам код для получения значения Order или Rank [производного] непосредственно из ваших базовых таблиц, независимо от столбца Order.

  5. После этого назначение Order тривиально. Другой способ заявить, что я бы заменил ваш первый SELECT и выполнил обе функции в одном сегменте кода; нет необходимости в COUNT() как в отдельном сегменте кода.

Вы заинтересованы?

В этом случае мне нужно знать точные типы col_N и на чем основано значение Order, например. высшая студенческая оценка. Определение таблицы (запутанное) проще.

Ответ на комментарии

  1. Well, if it is a temp table, there is a much easier way, a different approach:
    • Give it an Order column
    • определите его как IDENTITY
    • убедитесь, что SELECT, который вы используете для его заполнения, имеет правильный ORDER BY
      .
  2. Вам все еще нужно вычислить Order ?
    Если да, то извините, я не знаю, как сделать этот звук более приятным. Я понимаю, что вы думаете, что мне не нужно знать ответ на мой вопрос; но код, который я использую, выходит за этот порог, иначе вы бы уже написали его (похоже, у вас есть некоторый опыт работы с SQL и Sybase).

    Я не «чертую», у меня код уже 20 лет. В каждом случае мне просто нужно указать конкретные имена таблиц и столбцов, что занимает 10 секунд. Поэтому я знаю, что нужно коду (например, я понимаю логический принцип, который использует код, сам код предельно прост). И, конечно же, есть варианты, потому что это логичное решение, а не просто кусок умного кода: один человек хочет, чтобы Студенты оценивались по Марку; кто-то еще хочет два столбца со вторым DESCending.

    • Объяснение проблемы всегда занимает больше времени, чем ее устранение.

    • Например. у вас 2 столбца (с описанием все в порядке, спасибо). Но вы запрашиваете порядковый номер, для которого (по определению) требуется только один столбец или совокупность. Представьте уникальный кластеризованный индекс на (col_1, col2), который, как вы говорите, должен идентифицировать Order. Я не понимаю, почему col_2 актуально. Если порядковый номер строки, где col_1 равен "KKKKKK", равен 12345, то порядковый номер строки "KKKKKL" должен быть равен 12346; независимо от того, что такое col_2 в любой из этих двух строк. Это ведущий столбец индекса.

    • Код, который я предоставляю, должен учитывать все, что вы логически делаете в этом предложении ORDER BY. В противном случае он будет «незавершенным» или «незавершенным», и вам все равно придется с ним возиться; что не будет справедливо по отношению к вам, потому что вы не понимаете лежащего в его основе логического принципа.

    • Тогда у вас есть сложности из-за использования VARCHARs, который легко кодировать, но мне нужно знать, что это такое. Используя мою визуализацию Clustered Index, какой из этих четырех вариантов правильный? Мне нужно LTRIM() ? ( col_1 , col2)
      ( CONVERT( CHAR(30), col_1 ) , CONVERT( CHAR(30), col_2 ) )
      ( col_1 + col_2 ) -- as varchars
      ( CONVERT( CHAR(30), col_1 ) + CONVERT( CHAR(30), col_2 ) )

Ваше здоровье

person PerformanceDBA    schedule 14.12.2010
comment
в то время как вышеупомянутая проблема с нарушением 3NF верна для реальных таблиц, порядок был необходим для временной таблицы, где денормализация, очевидно, вообще не проблема. Хотя в целом хороший момент. - person DVK; 14.12.2010
comment
пожалуйста, не стесняйтесь попробовать и составить этот 5-строчный запрос. Конкретный пример в вопросе достаточно хорош (col_1 и col_2 равны varchar(30), предположим, что это просто случайные строки из 30 символов, а порядок сортировки должен соответствовать ORDER BY CASE WHEN col1="" THEN "AAAAAA" ELSE col1 END, ISNULL(col2, "ZZZ"). В английских терминах подумайте о них как о 30-значных числах в базе 26, представленных строками с заглавными буквами, с некоторыми NULL, которые должны рассматриваться как наименьшие # в случае col_1 и как наибольшие в случае col_2. - person DVK; 14.12.2010
comment
... Кстати, это связано с массированием существующих устаревших данных, прежде чем вы справедливо спросите WTF о дизайне. Я работал над проблемой, подпрыгивая на уровень выше и делая это на Perl, но я бы предпочел делать все это на SQL как по практическим причинам, так и в качестве упражнения по изучению SQL. - person DVK; 14.12.2010
comment
@ДВК. Формат комментария слишком ограничен. Ответил в моем ответе. - person PerformanceDBA; 15.12.2010

Надеюсь, это решение подходит.

Добавьте новое поле в таблицу T, например, ID

потом

обновить T set ID = number() из T порядка по Col1, Col2

Не уверен, какую версию Sybase вы используете, но это работает на 5.5.

Выбор * из T порядка по col1, col2 теперь будет давать тот же результат, что и выбор * из T порядка по ID

Ура АЛ

person AL Dwado    schedule 10.12.2010
comment
@ Аль Двадо - а? 5,5? У меня такое ощущение, что вы говорите о Sybase Anywhere — у Sybase Server никогда не было версии 5.5 IIRC. И, к сожалению, вопрос в контексте Sybase Server, у которого нет числовой функции (которая, как я подозреваю, специфична для Watcom SQL). - person DVK; 14.12.2010
comment
Да, это Sybase Anywhere. ASE также имеет эту функцию. Я не знал, что это Sybase Server. Другое решение заключается в следующем. Создайте временную таблицу с автоинкрементным первичным ключом, затем вставьте в эту таблицу из основной таблицы. - person AL Dwado; 15.12.2010
comment
объявить локальную временную таблицу TempT (целое число OrderNo по умолчанию AutoIncrement, col1 char (30), col2 char (30), первичный ключ (OrderNo)) при фиксации сохранить строки...THEN... вставить в TempT выбрать по умолчанию, Col1, Col2 из T заказать по (независимо) - person AL Dwado; 15.12.2010