2015-06-23 2 views
0

У меня есть база данных, содержащая версии версий программного обеспечения, и я хочу иметь возможность отбрасывать все версии, которые больше текущей версии, упорядоченной по номеру версии. Тем не менее, релизы сортируются в обычном (но стандартном) способе - от альфа-версии до бета-версии до основной версии для исправления. Так вот пример заказа:SQL больше пользовательского заказа

100a1 
100a4 
100b1 
100 
100p1 
101 
101p3 
etc. 

Можно ли сформировать запрос SQL, который тянет обратно эти данные, приведенных на заказ заказе или же> работает только для заданных порядков, как целые числа и даты? Я работаю с MSSQL, если это имеет значение.

+0

Всегда ли это 3 цифры, а затем необязательный символ плюс цифра? – jarlh

+0

Да, за исключением того, что окончательное число может быть одной или двух цифр. – Lisa

ответ

0

Вот мой пример кода. Не самый короткий, но он содержит много демо-ввода/вывода и может быть еще более упрощен, если вы понимаете, чего я хочу.

CREATE TABLE #versions(version nvarchar(10)) 

INSERT INTO #versions(version) 
VALUES(N'100a1'),(N'100a4'),(N'100b1'),(N'100p1'),(N'100'),(N'101'),(N'101p3') 

-- Just an example using substrings etc. how to get the 
SELECT version, 
    SUBSTRING(version,1, 
     CASE 
      WHEN PATINDEX(N'%[a-z]%',version) > 0 
      THEN PATINDEX(N'%[a-z]%',version)-1 
      ELSE LEN(version) 
     END 
    ) as version_number, 
    SUBSTRING(version, 
     CASE 
      WHEN PATINDEX(N'%[a-z]%',version) > 0 
      THEN PATINDEX(N'%[a-z]%',version) 
      ELSE 0 
     END, PATINDEX(N'%[0-9]%', 
      SUBSTRING(version,1, 
       CASE 
        WHEN PATINDEX(N'%[a-z]%',version) > 0 
        THEN PATINDEX(N'%[a-z]%',version)-1 
        ELSE LEN(version) 
       END 
      ) 
     ) 
    ) as version_suffix, 
    SUBSTRING(version, 
     PATINDEX(N'%[a-z]%', 
      SUBSTRING(version, 
       CASE 
        WHEN PATINDEX(N'%[a-z]%',version) > 0 
        THEN PATINDEX(N'%[a-z]%',version) 
        ELSE LEN(version) 
       END, LEN(version) 
      ) 
     ), 
     PATINDEX(N'%[0-9]%', 
      SUBSTRING(version,1, 
       CASE 
        WHEN PATINDEX(N'%[a-z]%',version) > 0 
        THEN PATINDEX(N'%[a-z]%',version)-1 
        ELSE LEN(version) 
       END 
      ) 
     ) 
    ) as version_sub 
FROM #versions 

-- Now your code: 
;WITH vNumber AS(
    SELECT version,SUBSTRING(version,1, 
     CASE 
      WHEN PATINDEX(N'%[a-z]%',version) > 0 
      THEN PATINDEX(N'%[a-z]%',version)-1 
      ELSE LEN(version) 
     END 
    ) as version_number 
    FROM #versions 
), vSuffix AS(
    SELECT version, SUBSTRING(version, 
       CASE 
        WHEN PATINDEX(N'%[a-z]%',version) > 0 
        THEN PATINDEX(N'%[a-z]%',version) 
        ELSE LEN(version) 
       END, LEN(version) 
      ) as version_suffix 
    FROM #versions 
) 
SELECT dat.version 
FROM (
    SELECT vn.version, vn.version_number, 
     CASE 
      SUBSTRING(vn.version, 
       CASE 
        WHEN PATINDEX(N'%[a-z]%',vn.version) > 0 
        THEN PATINDEX(N'%[a-z]%',vn.version) 
        ELSE 0 
       END, 1 
      ) 
      WHEN N'a' THEN 1 
      WHEN N'b' THEN 2 
      WHEN N'' THEN 3 
      WHEN N'p' THEN 4 
     END as version_suffix, 
     SUBSTRING(vn.version, 
      PATINDEX(N'%[a-z]%', 
       vs.version_suffix 
      ), 
      PATINDEX(N'%[0-9]%', 
       SUBSTRING(vn.version,1, 
        CASE 
         WHEN PATINDEX(N'%[a-z]%',vn.version) > 0 
         THEN PATINDEX(N'%[a-z]%',vn.version)-1 
         ELSE LEN(vn.version) 
        END 
       ) 
      ) 
     ) as version_sub 
    FROM vNumber as vn 
    INNER JOIN vSuffix as vs 
      ON vn.version = vs.version 
) AS dat 
ORDER BY dat.version_number, dat.version_suffix, dat.version_sub 

DROP TABLE #versions 

Это мой вход:

version 
---------- 
100a1 
100a4 
100b1 
100p1 
100 
101 
101p3 

И это результат:

version 
---------- 
100a1 
100a4 
100b1 
100 
100p1 
101 
101p3 

Во всяком случае. Я бы предложил разделить эти значения на отдельные столбцы. Это сделает вашу жизнь намного проще. :-)

0

Я предполагаю, что первые 3 позже - это число.

select * from tablename order by convert(int,left(Columnname,3)) 
+0

Это не сработает. Я проверил его. Просто авария из-за правильного заказа демо-данных. :-D Теперь я добавлю свое решение. – Ionic

+0

@Ionic Возможно, он работает для данной проблемы и данных OP. Как вы можете сказать, что это не сработает? Пожалуйста, дополните. –

+0

Я протестировал его. Просто измените положение 100p1 и 100. 100p1 будет заказано перед 100. Это неправильно. Пожалуйста, предоставьте только протестированный код. – Ionic

1

До тех пор, пока вы действительно можете описать, как должен работать заказ, обязательно.

два основных подхода:

  • Преобразовать значение в нечто порядкового. Например, вы можете использовать что-то вроде order by left([Version] + '__', 5). Работает и одно целое из более сложного значения.
  • Отделите значение на несколько значений, которые являются порядковыми номерами, и используйте все из них в order by в любом порядке. Это более идиоматический способ обработки этого в SQL - в основном, почему вы используете одно значение 101p1, когда вы логически работаете с 101, p, 1?

Разбор немного сложнее обрабатывать в SQL, поскольку SQL действительно предназначен для нормализованных наборов данных - и вы эффективно сохраняете несколько значений в одном столбце. Если ваши правила не слишком сложны, все равно это должно быть выполнимо. Это не будет ужасно красиво: D

Для фиксированных значений длины это, конечно, довольно просто - это эквивалент использования, например, 001p01 как имена файлов в файловой системе - в алфавитном порядке - правильный порядок. Затем вы можете просто использовать order by по целому значению или разбить его на части на основе substring s. Для значений с разделителями это немного уродливее, но все же довольно легко - 1.p.1 можно легко отделить, а затем вы можете упорядочить по каждой из частей в последовательности.

Однако ваша система, по-видимому, лучше подходит для людей, чем машины - нет никаких реальных намеков. В принципе, кажется, что вы смотрите на шаблон «числа, буквы, цифры ... обрабатываете числа как числа, а буквы - буквы». Это довольно сложно обрабатывать в T-SQL. Возможно, стоит принести помощь CLR и, в частности, регулярные выражения - я не уверен, что в любом случае вы сможете обрабатывать это в целом для неограниченного количества групп номеров/букв.

Простейший способ, по-видимому, состоит в том, чтобы просто отделить столбец версии от нескольких столбцов, каждый из которых имеет только одно значение - что-то вроде MajorVersion, Level, Revision или что-то в этом роде, соответствующее 101, Alpha, 3.

+1

Я применил второй подход в моем примере кода ниже. Но в основном я предлагаю иметь отдельные столбцы для этого :) – Ionic

Смежные вопросы