Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Limit LISTAGG results in Oracle

I'm trying to limit one of my columns in my SQL query that uses LISTAGG to only group the first 3 rows into a single column.

For instance:

Table
-----
Name   Orders
---------------
Joe    Joe_Order1
Joe    Joe_Order2
Joe    Joe_Order3
Joe    Joe_Order4
Joe    Joe_Order5
Joe    Joe_Order6
Mark   Mark_Order1
Mark   Mark_Order2
Mark   Mark_Order3
Mark   Mark_Order4

Have it return the following...

Name   Recent_Orders
-----------------------------
Joe    Joe_Order1, Joe_Order2, Joe_Order3
Mark   Mark_Order1, Mark_Order2, Mark_Order3

I'm able to concatenate the data using listagg however, I'm not entirely sure how to limit the results to the first 3 records.

SELECT NAME, LISTAGG(Orders, ', ') within group(order by Orders)
  as Recent_Orders
FROM
  Order_table
GROUP BY
  NAME

Is this possible with LISTAGG? Any help would be greatly appreciated. Thanks

like image 907
msleone Avatar asked May 30 '17 13:05

msleone


2 Answers

Apply a row_number in a CTE, then apply the restriction in the WHERE clause

with CTE as
(
select row_number() over(partition by NAME order by Orders) as rn,
       a1.*
from Order_Table a1
)
SELECT NAME, LISTAGG(Orders, ', ') within group(order by Orders)
  as Recent_Orders
FROM
  CTE
WHERE 
  rn <=3
GROUP BY
  NAME
like image 89
JohnHC Avatar answered Sep 25 '22 14:09

JohnHC


You can do this by enumerating the rows and using case:

SELECT NAME,
       LISTAGG(CASE WHEN seqnum <= 3 THEN Orders END, ', ') WITHIN GROUP (ORDER BY Orders) as Recent_Orders
FROM (SELECT o.*, ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY ?? DESC) as seqnum
      FROM Order_table o
     ) o
GROUP BY NAME;

By default, LISTAGG() ignores NULL values, so this does what you want.

The ?? is the column for specifying the ordering. SQL tables represent unordered sets; there is no "first three" or "last three" unless a column specifies the ordering.

like image 44
Gordon Linoff Avatar answered Sep 24 '22 14:09

Gordon Linoff