Переменная с ключевым словом TOP не может использоваться вместе - PullRequest
0 голосов
/ 12 декабря 2018

Так что в приведенном ниже запросе я получаю сообщение об ошибке, что рядом с ключевыми словами TOP и ORDER BY указан неверный синтаксис.Я не вижу проблемы с синтаксисом либо.После дальнейшего тестирования кажется, что моя платформа базы данных не позволяет использовать TOP и ORDER BY в производной таблице.Кто-нибудь видит обходной путь для этого запроса?Я не могу найти другой способ получить целочисленную переменную после TOP.Я использую неудачную платформу SqlDbx.

DECLARE @SQL VARCHAR(16384)
DECLARE @CC_N INT

SELECT 
    @CC_N = B.PP - A.CC_PP 
FROM
(
    SELECT 
        Carline, 
        SUM(PP_Floor) AS CC_PP
    FROM PP_Balancing
    WHERE Carline = '01'
    GROUP BY Carline
) AS A
JOIN PP_National AS B ON A.Carline = B.Carline

SET @SQL = 
'
UPDATE PP_Balancing
SET PP_Floor = PP_Floor + 1
WHERE
Sales_Locality IN
    (
        SELECT TOP '+CAST(@CC_N AS VARCHAR(255))+' 
            Sales_Locality
        FROM PP_Balancing
        WHERE Carline = ''01''
        ORDER BY PP_Decimal DESC
    )
AND Carline = ''01''
'

EXEC (@SQL)

1 Ответ

0 голосов
/ 12 декабря 2018

OP упомянул в комментариях, что это экземпляр Sybase ASE 12.5.4, поэтому мы рассмотрим некоторые специфические детали ASE ...

OP (пока) не предоставил фактическое сообщение об ошибке, ноЯ предполагаю, что это выглядит примерно так:

select * from
(select top 4 id
     from    sysobjects
     order by name
) dt
go

Msg 154, Level 15, State 53:
Server 'ASE200', Line 2:
An ORDER BY clause is not allowed in a derived table.

select * from
(select id
     from    sysobjects
     order by name
) dt
go

Msg 154, Level 15, State 53:
Server 'ASE200', Line 3:
An ORDER BY clause is not allowed in a derived table.

И это ожидаемое поведение (согласно Руководствам по Sybase ASE ), т. Е. order by не допускается в подпрограмме.запрос или производная таблица.

И хотя в подзапросе (или производной таблице) условие top допускается , результаты, скорее всего, будут не такими, как ожидалось (и не будут гарантированы).для получения одинаковых результатов при повторных запусках) без предложения order by.

Таким образом, остается более важный вопрос о том, как update просто число строк 'top X'.

Теперь Sybase ASE допускает предложение top в операторе update, но отсутствие поддержки предложения order by (в операторе update) делает top практически бесполезным, если, так какв этом случае необходимо применить желаемый порядок.

ПосколькуP использует переменную (@CC_N) для определения количества обновляемых строк, я собираюсь предположить, что мы можем использовать другую переменную для определения диапазона PP_Decimal значений, которые мы хотим обновить.

ДоЯ перехожу к фактическому выражению update, нам нужно рассмотреть пару промежуточных шагов ...

-- use variable (@name) to capture the Nth name from sysobjects (order by name)

select top 5 name from sysobjects order by name
go

 name
 ------------------------------
 sysalternates
 sysattributes
 syscolumns
 syscomments
 sysconstraints         <<<=== we want to capture this value in @name

(5 rows affected)

declare @name varchar(255)

-- @name will be assigned each value as it's returned by the query, with
-- the last value (sysconstraints) being the last value assigned to @name

select top 5 @name = name from sysobjects order by name

print @name
go

(5 rows affected)
sysconstraints          <<<=== the contents of @name

В этом примере я подключил статический 5, но в запросе OP нам нужноподключите переменную (@CC_N), которая потребует от нас динамического построения и выполнения запроса.Но в нашем случае это становится немного интереснее, так как для нашего динамического запроса нам также нужно записать результаты запроса в @name, чтобы мы могли использовать его позже.К счастью для нас, ASE позволяет нам сделать это, включив наш @name в динамически создаваемый запрос, например:

declare @name   varchar(30),
        @SQL    varchar(100),
        @CC_N   int

select  @CC_N = 5

select @SQL = 'select top ' + convert(varchar(30),@CC_N) + ' @name = name from sysobjects order by name'

select @SQL as 'my query'

exec(@SQL)

select @name as '@name'
go

 @SQL
 -------------------------------------------------------
 select top 5 @name = name from sysobjects order by name

 @name
 ------------------------------
 sysconstraints          <<<=== the contents of @name

На данный момент у нас должно быть все необходимое для реализации желаемого update.

ПРИМЕЧАНИЕ: Ради этого ответа я собираюсь предположить, что столбец PP_Decimal является целым числом.

DECLARE @SQL        varchar(1000),
        @CC_N       int,
        @PP_Decimal int

-- OPs original code to find the Nth value;
-- removed the superfluous 'group by' from the derived table

SELECT  @CC_N = B.PP - A.CC_PP 
FROM
(SELECT SUM(PP_Floor) AS CC_PP
 FROM   PP_Balancing
 WHERE Carline = '01'
) AS A
JOIN PP_National AS B ON A.Carline = B.Carline

-- ??? should OP check for @CC_N >= 1 ???

-- find the Nth PP_Decimal value where 'N' == @CC_N

select @SQL =
"select top " + convert(varchar(30), @CC_N) + " @PP_Decimal = PP_Decimal
 from   PP_Balancing
 where  Carline = '01'
 order by PP_Decimal desc"

-- comment-out/remove the following 'select';
-- only placed here for debugging purposes

select @SQL as '@SQL'

exec(@SQL)

-- at this point @PP_Decimal should contain the last/Nth PP_Decimal value when ordered by PP_Decimal desc;
-- again, following 'select' is for debugging purposes

select @PP_Decimal as '@PP_Decimal'

-- now update our table where PP_Decimal >= @PP_Decimal

update PP_Balancing
set    PP_Floor    = PP_Floor + 1
where  PP_Decimal >= @PP_Decimal
and    Carline     = '01'
go

 @SQL                                                                                                                                                                                                                                                                                                                                                                                                 
 ---------------------------------------
 select top 5 @PP_Decimal = PP_Decimal     <<<=== for sake of example I plugged in @CC_N=5
 from   PP_Balancing
 where  Carline = '01'
 order by PP_Decimal desc

 @PP_Decimal
 -----------
         538          <<<=== made up number for the sake of this example (since I don't have any actual data)

 (N rows affected)    <<<=== assuming update statement finds @CC_N rows to update

ПРИМЕЧАНИЕ. В этом решении предполагается, что значения PP_Decimalуникальный, в противном случае окончательные значения update могут обновить более @CC_N значений, например,

  • при условии max(PP_Decimal) = 47
  • при условии, что имеется 100 строк с PP_Decimal = 47
  • предполагается, что @CC_N = 5
  • @PP_Decimal будет установлен на 47 и
  • вместо того, чтобы воздействовать только на @ CC_N = 5 строк, update обновит все 100 строк, гдеPP_Decimal >= 47

Некоторые изменения, которые может предпринять пользователь, могут заключаться в ограничении количества обновляемых строк, например:

update top 5 ...

или

set rowcount 5
update ...
set rowcount 0

Но это не гарантирует, что одни и те же строки будут обновляться при повторных запусках.

Еще одно (очевидное) решение - это вытянутьЗначения столбца top @CC_N первичного ключа (PK) в таблицу #temp, а затем объединение update с этой таблицей #temp для выполнения требуемых обновлений @CC_N.Я подожду, чтобы посмотреть, является ли решение таблицы #temp приемлемым для OP, и / или пусть кто-то другой опубликует ответ с подробностями решения на основе таблицы #temp.

...