Мне нужно рассчитать разницу столбца между двумя строками таблицы. Есть ли способ сделать это прямо в SQL? Я использую Microsoft SQL Server 2008.
Я ищу что-то вроде этого:
SELECT value - (previous.value) FROM table
Представьте, что «предыдущая» переменная ссылается на последнюю выбранную строку. Конечно, с таким выбором я получу n-1 строк, выбранных в таблице с n строками, это не вероятно, на самом деле это именно то, что мне нужно.
Возможно ли это каким-то образом?
sql
sql-server
sql-server-2008
Эдвин Джарвис
источник
источник
Ответы:
В SQL нет встроенного понятия порядка, поэтому вам нужно упорядочить по некоторому столбцу, чтобы это было значимым. Что-то вроде этого:
select t1.value - t2.value from table t1, table t2 where t1.primaryKey = t2.primaryKey - 1
Если вы знаете, как упорядочивать вещи, но не знаете, как получить предыдущее значение с учетом текущего (например, вы хотите упорядочить в алфавитном порядке), то я не знаю, как сделать это в стандартном 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
Если вам нужно разорвать связи, вы можете добавить столько столбцов, сколько необходимо, в ORDER BY.
источник
Используйте функцию задержки :
SELECT value - lag(value) OVER (ORDER BY Id) FROM table
Последовательности, используемые для идентификаторов, могут пропускать значения, поэтому идентификатор-1 не всегда работает.
источник
LAG(ExpressionToSelect, NumberOfRowsToLag, DefaultValue)
. По умолчанию количество строк для задержки равно 1, но вы можете указать это и значение по умолчанию, которое нужно выбрать, когда невозможно отставать, поскольку вы находитесь в начале набора.Oracle, PostgreSQL, SQL Server и многие другие механизмы СУБД имеют аналитические функции, которые вызываются
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)
значительно улучшит этот запрос.источник
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
источник
LEFT JOIN таблица к самой себе, с отработанным условием соединения, так что строка, соответствующая в объединенной версии таблицы, находится на одну строку раньше, для вашего конкретного определения "previous".
Обновление: сначала я подумал, что вы хотите сохранить все строки с NULL для условия, когда не было предыдущей строки. Прочитав его снова, вы просто хотите, чтобы строки были выбраны, поэтому вам следует использовать внутреннее соединение, а не левое соединение.
Обновить:
В более новых версиях Sql Server также есть оконные функции LAG и LEAD, которые также можно использовать для этого.
источник
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
источник
Выбранный ответ будет работать только в том случае, если в последовательности нет пробелов. Однако, если вы используете автоматически сгенерированный идентификатор, в последовательности могут быть пробелы из-за отката вставок.
Этот метод должен работать, если у вас есть пробелы
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
источник