2009-04-02 2 views
55

Мне нужно рассчитать разницу в столбце между двумя строками таблицы, есть ли способ сделать это прямо в SQL? Я использую Microsoft SQL Server 2008.Есть ли способ получить доступ к значению «предыдущей строки» в инструкции SELECT?

Я ищу что-то вроде этого:

SELECT value - (previous.value) FROM table 

Воображая, что «предыдущий» ссылки на переменную последней выбранной строки. Разумеется, что с подобным выбором я выйду из n-1 строк, выбранных в таблице с n строками, это, вероятно, не совсем то, что мне нужно.

Возможно ли это каким-то образом?

+5

Ну, просто добавьте комментарий, полезный для новых зрителей. SQL 2012 имеет LAG и LEAD сейчас :) См. Эту ссылку http://blog.sqlauthority.com/2013/09/22/sql-server-how-to-access-the-previous-row-and-next-row- value-in-select-statement/ –

ответ

39

SQL не имеет встроенного понятия порядка, поэтому вам нужно заказать по какой-либо колонке, чтобы это имело смысл. Что-то вроде этого:

select t1.value - t2.value from table t1, table t2 
where t1.primaryKey = t2.primaryKey - 1 

Если вы знаете, как заказать вещи, но не как получить предыдущее значение с учетом текущего (EG, вы хотите заказать по алфавиту), то я не знаю способ сделать что в стандартном SQL, но большинство реализаций SQL будут иметь расширения для этого.

Вот способ для сервера SQL, который работает, если вы можете заказать строки таким образом, что каждый из них отличается:

select rank() OVER (ORDER BY id) as 'Rank', value into temp1 from t 

select t1.value - t2.value from temp1 t1, temp1 t2 
where t1.Rank = t2.Rank - 1 

drop table temp1 

Если вам необходимо разорвать связи, вы можете добавить столько столбцов, сколько необходимо для ЗАКАЗА ОТ.

+0

Это нормально, заказ не проблема, я просто удалил его из примера, чтобы упростить его, я попытаюсь это сделать. –

+4

, который предполагает, что первичные ключи генерируются последовательно, а строки никогда не удаляются, а в элементе выбора нет другого предложения заказа, а также ... – MartinStettner

+0

Мартин прав. Хотя это может работать в некоторых случаях, вам действительно нужно точно определить, что вы подразумеваете под «предыдущим» в бизнес-смысле, предпочтительно, не полагаясь на сгенерированный идентификатор. –

8

LEFT ПРИСОЕДИНИТЕСЬ к таблице, при условии, что условие соединения выполнено так, что строка, согласованная в объединенной версии таблицы, является одной строкой предыдущего, для вашего конкретного определения «предыдущий».

Обновление: Сначала я думал, что вы хотите сохранить все строки с NULL для условия, когда предыдущей строки не было. Читая его снова, вы просто хотите, чтобы строки были отбракованы, поэтому вы должны использовать внутреннее соединение, а не левое соединение.


Обновление:

Новые версии Sql Server также имеют ВЕДУЩИЕ функции Windowing Отставание и которые могут быть использованы для этого тоже.

24

Oracle, PostgreSQL, SQL Server и многие другие системы RDBMS имеют аналитические функции, называемые LAG и LEAD, которые делают именно это.

В SQL Server до 2012 года вы должны сделать следующее:

SELECT value - (
     SELECT TOP 1 value 
     FROM mytable m2 
     WHERE m2.col1 < m1.col1 OR (m2.col1 = m1.col1 AND m2.pk < m1.pk) 
     ORDER BY 
       col1, pk 
     ) 
FROM mytable m1 
ORDER BY 
     col1, pk 

, где COL1 является столбец вы заказываете на.

Имея индекс на (COL1, PK), вы значительно улучшите этот запрос.

+13

В SQL Server 2012 теперь есть LAG и LEAD. – ErikE

+0

Сценарий Hana SQL также поддерживает LAG и LEAD. – mik

+0

Просто добавьте еще один комментарий зрителям, которые прибыли сюда, ища это в Улье. Он также имеет функции LAG и LEAD. Документация здесь: https://cwiki.apache.org/confluence/display/Hive/LanguageManual+WindowingAndalytics –

2

Выбранный ответ будет работать только в том случае, если в последовательности нет пробелов. Однако, если вы используете автогенерированный идентификатор, скорее всего, будут пробелы в последовательности из-за вставленных вставок.

Этот метод должен работать, если у вас есть пробелы

declare @temp (value int, primaryKey int, tempid int identity) 
insert value, primarykey from mytable order by primarykey 

select t1.value - t2.value from @temp t1 
join @temp t2 
on t1.tempid = t2.tempid - 1 
23
WITH CTE AS (
    SELECT 
    rownum = ROW_NUMBER() OVER (ORDER BY columns_to_order_by), 
    value 
    FROM table 
) 
SELECT 
    curr.value - prev.value 
FROM CTE cur 
INNER JOIN CTE prev on prev.rownum = cur.rownum - 1 
+0

Работает правильно, если в запросе нет группировки, но что, если мы хотим вычесть значения из предыдущего значения только внутри группы , скажем, тот же EmployeeID, тогда как мы можем это сделать? Coz работает, это работает только для двух верхних строк каждой группы, а не для остальных строк в этой группе. Для этого я использовал этот код во время цикла, но это кажется очень медленным. Любой другой подход, который мы могли бы использовать в этом сценарии? И это тоже только в SQL Server 2008? –

2
select t2.col from (
select col,MAX(ID) id from 
(
select ROW_NUMBER() over(PARTITION by col order by col) id ,col from testtab t1) as t1 
group by col) as t2 
26

Используйте lag функции:

SELECT value - lag(value) OVER (ORDER BY Id) FROM table 

Последовательности, используемые для идентификаторов могут пропустить значения, так Id-1 делает не всегда работают.

+1

Это решение PostgreSQL. Речь идет о MSSQL. MSSQL имеет такую ​​функцию в версиях 2012+ (https://msdn.microsoft.com/en-us/en-en/library/hh231256(v=sql.120).aspx) – Kromster

+2

@KromStern Не только решение PostgreSQL. [Функции окна SQL] (https://www.simple-talk.com/sql/t-sql-programming/window-functions-in-sql/) были введены в [SQL: 2003] (https: // ru. wikipedia.org/wiki/SQL: 003). –

+1

Другими словами, будет ли это работать на случае OP? – Kromster

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