Row_number analytic function
Содержание:
Общие замечанияGeneral Remarks
Нет гарантии того, что строки, возвращенные запросом, использующим , будут расставлены в одинаковом порядке после каждого выполнения, если не соблюдены указанные ниже условия.There is no guarantee that the rows returned by a query using will be ordered exactly the same with each execution unless the following conditions are true.
-
Все значения в секционированном столбце являются уникальными.Values of the partitioned column are unique.
-
Все значения в столбцах являются уникальными.Values of the columns are unique.
-
Сочетания значений из столбца секционирования и столбцов являются уникальными.Combinations of values of the partition column and columns are unique.
Функция не детерминирована. is nondeterministic. Дополнительные сведения см. в разделе Deterministic and Nondeterministic Functions.For more information, see Deterministic and Nondeterministic Functions.
Функция NTILE
Эта функция позволяет разбивать строки в секции окна на примерно равные по размеру подгруппы (tiles) в соответствии с заданным числом подгрупп и упорядочением окна. Допустим, что нужно разбить строки представления OrderValues на 10 подгрупп одинакового размера на основе упорядочения по val. В представлении 830 строк, поэтому требуется 10 подгрупп, размер каждой будет составлять 83 (830 деленное на 10). Поэтому первым 83 строкам (одной десятой части), упорядоченным по val, будет назначен номер группы 1, следующим 83 строкам — номер подгруппы 2 и т. д. Вот запрос, вычисляющий номера как строк, так и подгрупп:
Если вы думаете, что разбиение на подгруппы похоже на разбиение на страницы, хочу вас предупредить, что не стоит их путать. При разбиении на страницы, размер страницы является константой, а число страниц меняется динамически — оно определяется делением числа строк в результате запроса на размер страницы. При разбиении на подгруппы число подгрупп является константой, а размер подгруппы меняется и определяется как число строк деленное на заданное число подгрупп. Ясно, для чего нужно разбиение на страницы, а разбиение на подгруппы обычно используется для аналитических задач — когда нужно распределить данные среди заданного числа равных по размеру сегментов с использованием упорядочения по определенному измерению.
Но вернемся к результату запроса, вычисляющего номера как строк, так и подгрупп: как видите они тесно связаны друг с другом. По сути, можно считать, что номер подгруппы вычисляется на основе номера строки. В предыдущем разделе мы говорили, что если упорядочение окна не является уникальным, функция ROW_NUMBER является недетерминистической. Если разбиение на подгруппы принципиально основано на номерах строк, то это означает, что вычисление NTILE также недетерминистично, если упорядочение окна не уникально. Это означает, что у данного запроса может быть несколько правильных результатов. Можно посмотреть на это с другой стороны: двум строкам с одним значением упорядочения могут быть назначены разные номера подгрупп. Если нужен гарантированный детерминизм, можно следовать моим рекомендациям по получению детерминистических номеров строк, а именно добавить в упорядочение окна дополнительный параметр:
Теперь у запроса только один правильный результат. Ранее, при описании функции NTILE я пояснил, что она позволяет разбить строки в секции окна на примерно равные подгруппы. Я использовал слово «примерно», потому что число строк, полученное в базовом запросе, может не делиться нацело на число подгрупп. Допустим, вы хотите разбить строки представления OrderValues на 100 подгрупп. При делении 830 на 100 получаем частное 8 и остаток 30. Это означает, что базовая размерность подгрупп будет 8, но часть подгрупп получать дополнительную строку. Функция NTILE не пытается распределять дополнительные строки среди подгрупп с равным расстоянием между подгруппами — она просто добавляет по одному ряду в первые подгруппы, пока не распределит остаток. При наличии остатка 30 размерность первых 30 подгрупп будет на единицу больше базовой размерности. Поэтому первые 30 будут содержать 9 рядов, а последние 70 — 8, как показано в следующем запросе:
Следуя привычному методу, попытаемся создать альтернативные решения, заменяющие функцию NTILE и не содержащие оконных функций.
Я покажу один способ решения задачи. Для начала, вот код, который вычисляет число подгрупп по заданным размерности, числу подгрупп и числу строк:
Вычисление вполне очевидно. Для входных данных код возвращает 5 в качестве числа подгрупп.
Затем применим эту процедуру к строкам представления OrderValues. Используйте агрегат COUNT, чтобы получить размерность результирующего набора, а не входные данные @cnt, а также примените описанную ранее логику для вычисления номеров строк без использования оконных функций вместо входных данных @rownum:
Как обычно, не пытайтесь повторить это в производственной среде! Это пример предназначен для обучения, а его производительность в SQL Server ужасна по сравнению с функцией NTILE.
What Is Oracle ROW_NUMBER?
Oracle ROW_NUMBER is an Oracle analytic function. It assigns a number to each row, depending on the parameters to the function, which define the ordering within a group of rows.
Many analytic functions follow a similar syntax, and ROW_NUMBER is one of those.
Let’s see an example of the Oracle ROW_NUMBER function.
Result:
| FIRST_NAME | LAST_NAME | ADDRESS_STATE | ROW_NUMBER_VALUE |
| Mark | Anderson | California | 1 |
| Susan | Johnson | Colorado | 1 |
| Robert | Pickering | Colorado | 2 |
| Michelle | Randall | Florida | 1 |
| Tom | Capper | Nevada | 1 |
| John | Rogers | Nevada | 2 |
| Mark | Holloway | New York | 1 |
| John | Smith | New York | 2 |
| Steven | Webber | New York | 3 |
| Mary | Taylor | Oregon | 1 |
| Julie | Armstrong | Texas | 1 |
| Andrew | Cooper | Texas | 2 |
| Tanya | Hall | Texas | 3 |
| Jarrad | Winston | Utah | 1 |
As you can see, it shows all of the records in the student table. It has ordered the records by address_state, and then by last_name.
For each record that has the same address_state value, a different number has been assigned. This is based on the records being ordered by the last_name value, then assigned a sequential number.
Susan Johnson has a value of 1, and Robert Pickering has a value of 2, because they are both in Colorado but Johnson comes before Pickering. Mark Anderson also has a value of 1, but the address_state is different, so this is OK.
Demo
In this section, we’ll take a look at the SQL ROW_NUMBER function. For the entire demo, I’ve used AdventureWorks2016 database.
How to use ROW_NUMBER in SQL Query
The following examples, we’ll see the use of OVER clause.
Let us get the list of all the customers by projecting the columns such as SalesOrderID, OrderDate, SalesOrderNumber, SubTotal, TotalDue and RowNum. The Row_Number function is applied with the order of the CustomerID column. The temporary value starts from 1 assigned based on the order of the CustomerID, and the values are continued till the last rows of the table. The order of CustomerID is not guaranteed because we don’t specify the ORDER BY clause in the query.
|
1 |
USEAdventureWorks2016; GO SELECTROW_NUMBER()OVER( ORDERBYCustomerID)ASRowNum, CustomerID, SalesOrderID, OrderDate, SalesOrderNumber, SubTotal, TotalDue FROMSales.SalesOrderHeader; |
How to use Order by clause
The following example uses the ORDER BY clause in the query. The ORDER BY clause in the query applied on the SalesOrderID column. We can see that the rows in output are still ordered and returned. The Row_Number is still applied to the CustomerID. The output indicates that the ORDER BY of the query and the ORDER BY of the OVER Clause are independent of the output.
|
1 |
USEAdventureWorks2016; GO SELECTROW_NUMBER()OVER( ORDERBYCustomerID)ASRowNum, CustomerID, SalesOrderID, OrderDate, SalesOrderNumber, SubTotal, TotalDue FROMSales.SalesOrderHeader ORDERBYSalesOrderID; |
How to use multiple columns with the OVER clause
The following example you can see that we have listed customerID and OrderDate in the ORDER BY clause. This gives the customer details with the most recent order details along with the sequence of numbers assigned to the entire result-set.
|
1 |
USEAdventureWorks2016; GO SELECTROW_NUMBER()OVER(ORDERBYCustomerID,OrderDateDESC)ASRowNum, CustomerID, SalesOrderID, OrderDate, SalesOrderNumber, SubTotal, TotalDue FROMSales.SalesOrderHeader |
How to use the SQL ROW_NUMBER function with PARTITION
The following example uses PARTITION BY clause on CustomerID and OrderDate fields. In the output, you can see that the customer 11019 has three orders for the month 2014-Jun. In this case, the partition is done on more than one column.
The partition is a combination of OrderDate and CustomerID. The Row_Number will start over for each unique combination of OrderDate and CustomerID. In this way, it’s easy to find the customer who has placed more than one order on the same day.
|
1 |
USEAdventureWorks2016; GO SELECTROW_NUMBER()OVER(PARTITIONBYCustomerID, DATEADD(MONTH,DATEDIFF(MONTH,,OrderDate),) ORDERBYSubTotalDESC)ASMonthlyOrders, CustomerID, SalesOrderID, OrderDate, SalesOrderNumber, SubTotal, TotalDue FROMSales.SalesOrderHeader; |
How to return a subset of rows using CTE and ROW_NUMBER
The following example we are going to analyze SalesOrderHeader to display the top five largest orders placed by each customer every month. Using the Month function, the orderDate columns is manipulated to fetch the month part. In this way, the sales corresponding to specific month (OrderDate) along with customer (CustomerID) is partitioned.
To list the five largest orders in each month for each customer, a CTE is used. A window is created on the partition data and it is assigned with the values and then the CTE is being called to fetch the largest orders.
|
1 |
WITHcte AS(SELECTROW_NUMBEROVER(PARTITIONBYcustomerID,MONTH(OrderDate)ORDERBYSubTotalDESC,TotalDueDESC)ASROW_NUM, CustomerID, MONTH(OrderDate)Month, SubTotal, TotalDue, OrderDate FROMSales.SalesOrderHeader ) SELECT* FROMcte WHEREROW_NUM<=5 |
What Is Oracle ROWNUM?
Oracle ROWNUM is a pseudocolumn that assigns a number to each row returned by a query. It’s assigned before an ORDER BY is performed, so you shouldn’t order by the ROWNUM value.
You might think that ROWNUM is a function in Oracle.
However, it’s not a function. It’s a “pseudocolumn”. It acts like a column but it’s not defined on the table.
ROWNUM assigns a number to each row returned by a query. It is assigned before an ORDER BY is performed.
For now, let’s see an example of the Oracle ROWNUM pseudocolumn.
Result:
| ROWNUM | FIRST_NAME | LAST_NAME | ADDRESS_STATE |
| 1 | John | Smith | New York |
| 2 | Susan | Johnson | Colorado |
| 3 | Tom | Capper | Nevada |
| 4 | Mark | Holloway | New York |
| 5 | Steven | Webber | New York |
| 6 | Julie | Armstrong | Texas |
| 7 | Michelle | Randall | Florida |
| 8 | Andrew | Cooper | Texas |
| 9 | Robert | Pickering | Colorado |
| 10 | Tanya | Hall | Texas |
| 11 | Jarrad | Winston | Utah |
| 12 | Mary | Taylor | Oregon |
| 13 | Mark | Anderson | California |
| 14 | John | Rogers | Nevada |
As you can see, the ROWNUM value has been added as the first column of the result set.
Общие замечанияGeneral Remarks
В одном запросе с одним предложением FROM может использоваться несколько оконных функций.More than one window function can be used in a single query with a single FROM clause. Предложение OVER для каждой функции может отличаться в части секционирования и упорядочения.The OVER clause for each function can differ in partitioning and ordering.
Если PARTITION BY не указан, функция обрабатывает все строки результирующего набора запроса как одну группу.If PARTITION BY is not specified, the function treats all rows of the query result set as a single group.
Важно!Important!
Если указано предложение ROWS или RANGE и используется <window frame preceding> для <window frame extent> (короткий синтаксис), то данная спецификация используется в качестве начальной точки границы рамки окна, а CURRENT ROW — в качестве конечной точки границы окна.If ROWS/RANGE is specified and <window frame preceding> is used for <window frame extent> (short syntax) then this specification is used for the window frame boundary starting point and CURRENT ROW is used for the boundary ending point. Например «ROWS 5 PRECEDING» равно «ROWS BETWEEN 5 PRECEDING AND CURRENT ROW».For example «ROWS 5 PRECEDING» is equal to «ROWS BETWEEN 5 PRECEDING AND CURRENT ROW».
Примечание
Если предложение ORDER BY не указано, то для рамки окна используется весь раздел.If ORDER BY is not specified entire partition is used for a window frame. Это относится только к тем функциям, которым не требуется предложение ORDER BY.This applies only to functions that do not require ORDER BY clause. Если предложение ROWS или RANGE не указаны, а указано предложение ORDER BY, то в качестве значения по умолчанию для рамки окна используется RANGE UNBOUNDED PRECEDING AND CURRENT ROW.If ROWS/RANGE is not specified but ORDER BY is specified, RANGE UNBOUNDED PRECEDING AND CURRENT ROW is used as default for window frame. Это относится только к тем функциям, которые могут принимать дополнительную спецификацию ROWS или RANGE.This applies only to functions that have can accept optional ROWS/RANGE specification. Например, ранжирующая функция не может принимать предложение ROWS или RANGE, поэтому данная рамка окна не может использоваться, даже несмотря на наличие предложения ORDER BY, а предложение ROWS или RANGE отсутствует.For example, ranking functions cannot accept ROWS/RANGE, therefore this window frame is not applied even though ORDER BY is present and ROWS/RANGE is not.
MySQL ROW_NUMBER() syntax
MySQL introduced the function since version 8.0. The is a window function or analytic function that assigns a sequential number to each row to which it applied beginning with one.
Notice that if you use MySQL with version less than 8.0, you can emulate some functionality of the function using various techniques.
The following shows the syntax of the function:
partition_definition
The has the following syntax:
The clause breaks the rows into smaller sets. The expression can be any valid expression that would be used in the clause. You can use multiple expressions separated by commas.
The clause is optional. If you omit it, the entire result set is considered a partition. However, when you use the clause, each partition can be also considered as a window.
order_definition
The syntax looks like the following:
The purpose of the clause is to set the orders of rows. This clause is independent of the clause of the query.
SQL Справочник
SQL Ключевые слова
ADD
ADD CONSTRAINT
ALTER
ALTER COLUMN
ALTER TABLE
ALL
AND
ANY
AS
ASC
BACKUP DATABASE
BETWEEN
CASE
CHECK
COLUMN
CONSTRAINT
CREATE
CREATE DATABASE
CREATE INDEX
CREATE OR REPLACE VIEW
CREATE TABLE
CREATE PROCEDURE
CREATE UNIQUE INDEX
CREATE VIEW
DATABASE
DEFAULT
DELETE
DESC
DISTINCT
DROP
DROP COLUMN
DROP CONSTRAINT
DROP DATABASE
DROP DEFAULT
DROP INDEX
DROP TABLE
DROP VIEW
EXEC
EXISTS
FOREIGN KEY
FROM
FULL OUTER JOIN
GROUP BY
HAVING
IN
INDEX
INNER JOIN
INSERT INTO
INSERT INTO SELECT
IS NULL
IS NOT NULL
JOIN
LEFT JOIN
LIKE
LIMIT
NOT
NOT NULL
OR
ORDER BY
OUTER JOIN
PRIMARY KEY
PROCEDURE
RIGHT JOIN
ROWNUM
SELECT
SELECT DISTINCT
SELECT INTO
SELECT TOP
SET
TABLE
TOP
TRUNCATE TABLE
UNION
UNION ALL
UNIQUE
UPDATE
VALUES
VIEW
WHERE
MySQL Функции
Функции строк
ASCII
CHAR_LENGTH
CHARACTER_LENGTH
CONCAT
CONCAT_WS
FIELD
FIND_IN_SET
FORMAT
INSERT
INSTR
LCASE
LEFT
LENGTH
LOCATE
LOWER
LPAD
LTRIM
MID
POSITION
REPEAT
REPLACE
REVERSE
RIGHT
RPAD
RTRIM
SPACE
STRCMP
SUBSTR
SUBSTRING
SUBSTRING_INDEX
TRIM
UCASE
UPPER
Функции чисел
ABS
ACOS
ASIN
ATAN
ATAN2
AVG
CEIL
CEILING
COS
COT
COUNT
DEGREES
DIV
EXP
FLOOR
GREATEST
LEAST
LN
LOG
LOG10
LOG2
MAX
MIN
MOD
PI
POW
POWER
RADIANS
RAND
ROUND
SIGN
SIN
SQRT
SUM
TAN
TRUNCATE
Функции дат
ADDDATE
ADDTIME
CURDATE
CURRENT_DATE
CURRENT_TIME
CURRENT_TIMESTAMP
CURTIME
DATE
DATEDIFF
DATE_ADD
DATE_FORMAT
DATE_SUB
DAY
DAYNAME
DAYOFMONTH
DAYOFWEEK
DAYOFYEAR
EXTRACT
FROM_DAYS
HOUR
LAST_DAY
LOCALTIME
LOCALTIMESTAMP
MAKEDATE
MAKETIME
MICROSECOND
MINUTE
MONTH
MONTHNAME
NOW
PERIOD_ADD
PERIOD_DIFF
QUARTER
SECOND
SEC_TO_TIME
STR_TO_DATE
SUBDATE
SUBTIME
SYSDATE
TIME
TIME_FORMAT
TIME_TO_SEC
TIMEDIFF
TIMESTAMP
TO_DAYS
WEEK
WEEKDAY
WEEKOFYEAR
YEAR
YEARWEEK
Функции расширений
BIN
BINARY
CASE
CAST
COALESCE
CONNECTION_ID
CONV
CONVERT
CURRENT_USER
DATABASE
IF
IFNULL
ISNULL
LAST_INSERT_ID
NULLIF
SESSION_USER
SYSTEM_USER
USER
VERSION
SQL Server функции
Функции строк
ASCII
CHAR
CHARINDEX
CONCAT
Concat with +
CONCAT_WS
DATALENGTH
DIFFERENCE
FORMAT
LEFT
LEN
LOWER
LTRIM
NCHAR
PATINDEX
QUOTENAME
REPLACE
REPLICATE
REVERSE
RIGHT
RTRIM
SOUNDEX
SPACE
STR
STUFF
SUBSTRING
TRANSLATE
TRIM
UNICODE
UPPER
Функции чисел
ABS
ACOS
ASIN
ATAN
ATN2
AVG
CEILING
COUNT
COS
COT
DEGREES
EXP
FLOOR
LOG
LOG10
MAX
MIN
PI
POWER
RADIANS
RAND
ROUND
SIGN
SIN
SQRT
SQUARE
SUM
TAN
Функции дат
CURRENT_TIMESTAMP
DATEADD
DATEDIFF
DATEFROMPARTS
DATENAME
DATEPART
DAY
GETDATE
GETUTCDATE
ISDATE
MONTH
SYSDATETIME
YEAR
Функции расширений
CAST
COALESCE
CONVERT
CURRENT_USER
IIF
ISNULL
ISNUMERIC
NULLIF
SESSION_USER
SESSIONPROPERTY
SYSTEM_USER
USER_NAME
MS Access функции
Функции строк
Asc
Chr
Concat with &
CurDir
Format
InStr
InstrRev
LCase
Left
Len
LTrim
Mid
Replace
Right
RTrim
Space
Split
Str
StrComp
StrConv
StrReverse
Trim
UCase
Функции чисел
Abs
Atn
Avg
Cos
Count
Exp
Fix
Format
Int
Max
Min
Randomize
Rnd
Round
Sgn
Sqr
Sum
Val
Функции дат
Date
DateAdd
DateDiff
DatePart
DateSerial
DateValue
Day
Format
Hour
Minute
Month
MonthName
Now
Second
Time
TimeSerial
TimeValue
Weekday
WeekdayName
Year
Другие функции
CurrentUser
Environ
IsDate
IsNull
IsNumeric
SQL ОператорыSQL Типы данныхSQL Краткий справочник
示例Examples
A.A. 简单示例Simple examples
以下查询按字母顺序返回四个系统表。The following query returns the four system tables in alphabetic order.
下面是结果集:Here is the result set.
| namename | recovery_model_descrecovery_model_desc |
|---|---|
| 主master | SIMPLESIMPLE |
| 模型model | FULLFULL |
| msdbmsdb | SIMPLESIMPLE |
| tempdbtempdb | SIMPLESIMPLE |
要在每行的前面添加一个行编号列,请使用 函数添加一个列(此示例中名为 )。To add a row number column in front of each row, add a column with the function, in this case named . 必须将 子句向前移动到 子句处。You must move the clause up to the clause.
下面是结果集:Here is the result set.
| Row#Row# | namename | recovery_model_descrecovery_model_desc |
|---|---|---|
| 11 | 主master | SIMPLESIMPLE |
| 22 | 模型model | FULLFULL |
| 33 | msdbmsdb | SIMPLESIMPLE |
| 44 | tempdbtempdb | SIMPLESIMPLE |
若是在 列上添加 子句,当 值发生更改时将重新开始编号。Adding a clause on the column, will restart the numbering when the value changes.
下面是结果集:Here is the result set.
| Row#Row# | namename | recovery_model_descrecovery_model_desc |
|---|---|---|
| 11 | 模型model | FULLFULL |
| 11 | 主master | SIMPLESIMPLE |
| 22 | msdbmsdb | SIMPLESIMPLE |
| 33 | tempdbtempdb | SIMPLESIMPLE |
B.B. 返回销售人员的行号Returning the row number for salespeople
以下示例根据销售人员年初至今的销售额,计算 Adventure Works CyclesAdventure Works Cycles 中销售人员的行号。The following example calculates a row number for the salespeople in Adventure Works CyclesAdventure Works Cycles based on their year-to-date sales ranking.
下面是结果集:Here is the result set.
C.C. 返回行的子集Returning a subset of rows
下面的示例按 的顺序计算 表中所有行的行号,并只返回行 到 (含)。The following example calculates row numbers for all rows in the table in the order of the and returns only rows to inclusive.
D.D. 将 ROW_NUMBER () 与 PARTITION 一起使用Using ROW_NUMBER() with PARTITION
以下示例使用 参数按列 对结果集进行分区。The following example uses the argument to partition the query result set by the column . 在 子句中指定的 子句按列 对每个分区中的行进行排序。The clause specified in the clause orders the rows in each partition by the column . 语句中的 按 子句对整个查询结果集进行排序。The clause in the statement orders the entire query result set by .
下面是结果集:Here is the result set.