Group function is nested too deeply SQL Error - sql

I have a table which looks like this:
+-----------------+--------------+
| Field | Type |
+-----------------+--------------+
| orderNumber (PK)| int |
| orderDate | date |
| requiredDate | date |
| shippedDate | date |
| status | char(15) |
| comments | char(200) |
| customerNumber | int |
+-----------------+--------------+
I need to return the customerNumber which has maximum number of orders.
I tried the following command:
SELECT customerNumber FROM ORDERS WHERE customerNumber IN (SELECT customerNumber FROM ORDERS HAVING MAX(COUNT(customerNumber)) GROUP BY customerNumber);
I think an error: group function is nested too deeply

SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE ORDERS (
orderNumber int PRIMARY KEY,
orderDate date,
requiredDate date,
shippedDate date,
status char(15),
comments char(200),
customerNumber int
);
INSERT INTO ORDERS ( ORDERNUMBER, CUSTOMERNUMBER ) VALUES ( 1, 1 );
INSERT INTO ORDERS ( ORDERNUMBER, CUSTOMERNUMBER ) VALUES ( 2, 1 );
INSERT INTO ORDERS ( ORDERNUMBER, CUSTOMERNUMBER ) VALUES ( 3, 2 );
INSERT INTO ORDERS ( ORDERNUMBER, CUSTOMERNUMBER ) VALUES ( 4, 2 );
INSERT INTO ORDERS ( ORDERNUMBER, CUSTOMERNUMBER ) VALUES ( 5, 3 );
INSERT INTO ORDERS ( ORDERNUMBER, CUSTOMERNUMBER ) VALUES ( 6, 4 );
Query 1 - If you only want to get a single customer:
SELECT CUSTOMERNUMBER
FROM (
SELECT CUSTOMERNUMBER,
COUNT( ORDERNUMBER ) AS num_orders
FROM ORDERS
GROUP BY CUSTOMERNUMBER
ORDER BY num_orders DESC
)
WHERE ROWNUM = 1
Results:
| CUSTOMERNUMBER |
|----------------|
| 1 |
Query 2 - If you want to get all customers with the highest number of orders:
SELECT CUSTOMERNUMBER
FROM (
SELECT CUSTOMERNUMBER,
RANK() OVER ( ORDER BY NUM_ORDERS DESC ) AS RNK
FROM (
SELECT CUSTOMERNUMBER,
COUNT( ORDERNUMBER ) AS num_orders
FROM ORDERS
GROUP BY CUSTOMERNUMBER
ORDER BY num_orders DESC
)
)
WHERE RNK = 1
Results:
| CUSTOMERNUMBER |
|----------------|
| 1 |
| 2 |

One way to do it is using ctes, where you get the count of orders in the first cte, then select the maximum value. Finally join them to get the customer with the maximum orders.
with ordercount as (select customernumber, count(distinct ordernumber) ordercount
from orders
group by customernumber)
,maxorders as (select max(ordercount) maxcount from ordercount)
select o.customernumber
from ordercount o
join maxorders m on m.maxcount = o.ordercount

Related

Select duplicates

I have a table with 3 columns like the sample below.
All rows have a unique productid but there are duplicates of customerid and productname together. I want to select only 1 record of each duplicate into a new table with all 3 columns. So from the rows below I want row 1 and 3 into the new table.
productid(guid) customerid productname
4362C96D-B413-EA11-A811-000D3A25C7C2 12345678910 credit
C7EC397D-04BF-E611-80EE-005056A027F8 12345678910 credit
F796026C-B413-EA11-A811-000D3A25C942 24681012141 leasing
7490976F-B413-EA11-A811-000D3A25C7C6 24681012141 leasing
I use this SQL to select all the duplicate rows into a new table:
SELECT p.productid, p2.customerid, p2.productname
INTO tempTable
FROM products AS p
JOIN (SELECT customerid, productname
FROM products
GROUP BY customerid, productname
HAVING COUNT(productname)>1) AS p2
ON p.customerid = p2.customerid AND p.productname= p2.productname
ORDER BY p.customerid, p.productname
This SQL works without the productid but won't find the duplicates if I add productid that is unique pr row.
SELECT customerid, productname
FROM testtable
GROUP BY customerid, productname
HAVING COUNT(productname) > 1
ORDER BY customerid
| 12345678910 | credit |
| 24681012141 | leasing |
How can I query this data to select only 1 of each duplicate row?
CREATE TABLE MyTable (productid varchar(255),customerid bigint, productname varchar(50))
INSERT INTO MyTable (productid,customerid,productname) VALUES
('4362C96D-B413-EA11-A811-000D3A25C7C2',12345678910,'credit'),
('C7EC397D-04BF-E611-80EE-005056A027F8',12345678910,'credit'),
('F796026C-B413-EA11-A811-000D3A25C942',24681012141,'leasing'),
('7490976F-B413-EA11-A811-000D3A25C7C6',24681012141,'leasing')
WITH CTE AS (
SELECT
productid,
customerid,
productname,
ROW_NUMBER() OVER (PARTITION BY customerid, productname ORDER BY productid) AS rn
FROM MyTable
)
SELECT customerid,
productname FROM CTE WHERE rn =1
GO
customerid | productname
----------: | :----------
12345678910 | credit
24681012141 | leasing
SELECT customerid, productname
FROM MyTable
GROUP BY customerid, productname
HAVING COUNT(*) > 1
ORDER BY customerid
GO
customerid | productname
----------: | :----------
12345678910 | credit
24681012141 | leasing
db<>fiddle here
You can add a ROW_NUMBER() windowing function to your result set to distinguish between the GUID values.
SELECT
productid,
customerid,
productname
INTO tempTable
SELECT
productid,
customerid,
productname
FROM
(
SELECT
productid,
customerid,
productname,
ROW_NUMBER() OVER (PARTITION BY customerid, productname ORDER BY productid) AS rn
FROM testtable
) AS d
WHERE d.rn = 1

How to find max value from each group and display their information when using "group by"

For example, i create a table about people contribue to 2 campaigns
+-------------------------------------+
| ID Name Campaign Amount (USD) |
+-------------------------------------+
| 1 A 1 10 |
| 2 B 1 5 |
| 3 C 2 7 |
| 4 D 2 9 |
+-------------------------------------+
Task: For each campaign, find the person (Name, ID) who contribute the most to
Expected result is
+-----------------------------------------+
| Campaign Name ID |
+-----------------------------------------+
| 1 A 1 |
| 2 D 4 |
+-----------------------------------------+
I used "group by Campaign" but the result have 2 columns "Campagin" and "max value" when I need "Name" and "ID"
Thanks for your help.
Edited: I fix some values, really sorry
You can use analytic functions for this:
select name, id, amount
from (select t.*, max(amount) over (partition by campaign) as max_amount
from t
) t
where amount = max_amount;
You can also do it by giving a rank/row_number partiton by campaign and order by descending order of amount.
Query
;with cte as(
select [num] = dense_rank() over(
partition by [Campaign]
order by [Amount] desc
), *
from [your_table_name]
)
select [Campaign], [Name], [ID]
from cte
where [num] = 1;
Try the next query:-
SELECT Campaign , Name , ID
FROM (
SELECT Campaign , Name , ID , MAX (Amount)
FROM MyTable
GROUP BY Campaign , Name , ID
) temp;
Simply use Where Clause with the max of amount group by Campaign:-
As following generic code:-
select a, b , c
from tablename
where d in
(
select max(d)
from tablename
group by a
)
Demo:-
Create table #MyTable (ID int , Name char(1), Campaign int , Amount int)
go
insert into #MyTable values (1,'A',1,10)
insert into #MyTable values (2,'B',1,5)
insert into #MyTable values (3,'C',2,7)
insert into #MyTable values (4,'D',2,9)
go
select Campaign, Name , ID
from #MyTable
where Amount in
(
select max(Amount)
from #MyTable
group by Campaign
)
drop table #MyTable
Result:-
Please find the below code for the same
SELECT *
FROM #MyTable T
OUTER APPLY (
SELECT COUNT(1) record
FROM #MyTable T1
where t.Campaign = t1.Campaign
and t.amount < t1.amount
)E
where E.record = 0

Select invoices based on quantity needed

I have a table that looks like this:
+---------------+---------------+------------------+--------------+
| InvoiceNumber | ProductNumber | ReceivedQuantity | ReceivedDate |
+---------------+---------------+------------------+--------------+
| INV001 | P001 | 500 | 09/01/2015 |
| INV002 | P001 | 600 | 09/02/2015 |
| INV003 | P001 | 700 | 09/03/2015 |
+---------------+---------------+------------------+--------------+
When a product is ordered. System needs to know which invoice it gets it from. First in first out.
For example I need 1000 quantity of product number P001. It should select the following invoices. It does not display the last invoice since 500 + 600 is already sufficient quantity
+---------------+---------------+------------------+--------------+
| InvoiceNumber | ProductNumber | ReceivedQuantity | ReceivedDate |
+---------------+---------------+------------------+--------------+
| INV001 | P001 | 500 | 09/01/2015 |
| INV002 | P001 | 600 | 09/02/2015 |
+---------------+---------------+------------------+--------------+
I can replicate this by making a cursor and looping through the table but looking for the best way to achieve this. Any nudge to the right direction would help a lot.
I think you can use a query like this:
;WITH t As (
SELECT *
, ROW_NUMBER() OVER (ORDER BY ReceivedDate, InvoiceNumber) As RowNo
FROM yourTable
), firstOverflow AS (
SELECT TOP(1)
t1.RowNo
FROM t t1
LEFT JOIN
t t2 ON t1.ProductNumber = t2.ProductNumber AND t1.ReceivedDate >= t2.ReceivedDate
GROUP BY t1.RowNo, t1.InvoiceNumber, t1.ProductNumber, t1.ReceivedQuantity, t1.ReceivedDate
HAVING SUM(t2.ReceivedQuantity) >= 1000
ORDER BY SUM(t2.ReceivedQuantity) - 1000)
SELECT *
FROM t
JOIN
firstOverflow ON t.RowNo <= firstOverflow.RowNo;
A better solution is this:
DECLARE #value int = 1000;
WITH t As (
SELECT *
, ROW_NUMBER() OVER (ORDER BY ReceivedDate, InvoiceNumber) As seq
FROM yourTable
), s As (
SELECT t.InvoiceNumber, t.ProductNumber, t.ReceivedQuantity, t.ReceivedDate, SUM(tt.ReceivedQuantity) As currentTotal
FROM t
LEFT JOIN
t tt ON t.ProductNumber = tt.ProductNumber AND t.seq >= tt.seq
GROUP BY t.InvoiceNumber, t.ProductNumber, t.ReceivedQuantity, t.ReceivedDate
), st As (
SELECT *
, ROW_NUMBER() OVER (ORDER BY (CASE WHEN s.currentTotal > #value THEN -currentTotal ELSE Null END) DESC) As seq
FROM s)
SELECT st.InvoiceNumber, st.ProductNumber, st.ReceivedQuantity, st.ReceivedDate
FROM st
WHERE currentTotal < #value
UNION ALL
SELECT st.InvoiceNumber, st.ProductNumber, st.ReceivedQuantity, st.ReceivedDate
FROM st
WHERE currentTotal >= #value AND st.seq = 1;
Try this query and give some feedback:
DECLARE #table TABLE (InvoiceNumber nvarchar(100),
ProductNumber nvarchar(100),
ReceivedQuantity int)
INSERT INTO #table VALUES ('inv001', 'p001', 500)
INSERT INTO #table VALUES ('inv002', 'p001', 600)
INSERT INTO #table VALUES ('inv003', 'p001', 600)
INSERT INTO #table VALUES ('inv004', 'p001', 600)
SQL 2012:
SELECT v.* FROM
(
SELECT t.*,
SUM(ReceivedQuantity) OVER (PARTITION BY ProductNumber ORDER BY InvoiceNumber) AS sum
FROM #table t
) v
WHERE sum <= 1000
SQL 2008:
SELECT v.* FROM
(
SELECT
a.InvoiceNumber
, a.ProductNumber
, SUM(b.ReceivedQuantity) AS sum
FROM
#table a
INNER JOIN #table b
ON a.InvoiceNumber >= b.InvoiceNumber AND a.ProductNumber = b.ProductNumber
GROUP BY
a.InvoiceNumber
, a.ProductNumber
) v
WHERE sum <= 1000

SQL - Previous order date per row

I have two tables:
Order (order_id, client_id, order_date)
Client (client_id)
where clients can have many orders
What I need is a previous_order(date) column per row
Example:
order_id | order_date | order_client | previous_order
-----------------------------------------------------
1 | 02/02 | 1 | null
2 | 02/03 | 1 | 02/02
3 | 02/10 | 1 | 02/03
4 | 02/10 | 1 | 02/10
I tried this command:
select a.*, b.previous_date from order a
join (select a.order_client, min(a.order_date) as previous_date from order a
group by a.order_client) b
on a.order_client = b.order_client
But using this i get the first order of each client.
I also saw lag() and lead() functions. But they are not avaiable in the microsoft sql server 10.50
Can anyone help me with this SQL command?
Thanks in advance
Perhaps this:
CREATE TABLE #Client (Client_ID INT )
CREATE TABLE #Order (Order_ID INT, Client_ID INT, Order_Date DATE)
INSERT INTO #Client
( Client_ID )
VALUES
( 1 ) -- Client_ID - int
INSERT INTO #Order
( Order_ID, Client_ID, Order_Date )
VALUES
( 1 , 1 , '20150202' ),
( 2 , 1 , '20150203' ),
( 3 , 1 , '20150210' ),
( 4 , 1 , '20150210' )
SELECT
Order_ID AS order_id
, Order_Date AS order_date
, Client_ID AS order_client
, (SELECT MAX(Ord2.Order_Date)
FROM #Order AS Ord2
WHERE
Ord2.Order_Date <= #Order.Order_Date
AND Ord2.Order_ID < #Order.Order_ID
AND Ord2.Client_ID = #Order.Client_ID) AS previous_order
FROM #Order
-- Cleanup
DROP TABLE #Client
DROP TABLE #Order

Selecting most recent record for each group with a sum function

This is a sample table
ID | Serial | Quantity | Date_Created
-------------------------------------
1 | AS1GD | 10 | 2014-12-25 8:00:00 AM
1 | GO9A4 | 5 | 2014-12-28 9:04:32 AM
2 | JF8WS | 15 | 2014-12-29 9:23:43 AM
2 | JFLE0 | 15 | 2015-01-04 10:53:12 AM
2 | S8A4A | 10 | 2015-01-05 9:12:46 AM
3 | FXOE3 | 20 | 2015-01-03 9:31:52 AM
3 | LSOR9 | 22 | 2015-01-06 12:00:44 PM
My expected result
ID | Serial | Total_Quantity | Last_DateCreated
-------------------------------------------------
1 | GO9A4 | 15 | 2014-12-28 9:04:32 AM
2 | S8A4A | 40 | 2015-01-05 9:12:46 AM
3 | LSOR9 | 42 | 2015-01-06 12:00:44 PM
Here's a query I tried but it's not returning the sum but only the quantity of the record
WITH total AS
( SELECT [ID], [date_created], [serial], sum(quantity) as qty,
ROW_NUMBER() OVER (PARTITION BY [ID] ORDER BY [date_created] DESC) AS rownum
FROM [table]
group by ID, date_created, serial
)
SELECT ID, Serial, qty, date_created
FROM total
WHERE rownum = 1
Since you are grouping by more than the ID but want the SUM() at the ID level, you can add OVER() to your SUM():
;WITH total AS ( SELECT [ID]
, [date_created]
, [serial]
, SUM(SUM(quantity)) OVER(PARTITION BY [ID]) as qty
, ROW_NUMBER() OVER (PARTITION BY [ID] ORDER BY [date_created] DESC) AS rownum
FROM [table]
GROUP BY ID, date_created, serial
)
SELECT ID, Serial, qty, date_created
FROM total
WHERE rownum = 1
The above creates an oddity in which you need two SUM() in order to use the OVER(), but you can ditch the GROUP BY altogether in your example:
;WITH total AS ( SELECT [ID]
, [date_created]
, [serial]
, SUM(quantity) OVER(PARTITION BY [ID]) as qty
, ROW_NUMBER() OVER (PARTITION BY [ID] ORDER BY [date_created] DESC) AS rownum
FROM Table1
)
SELECT ID, Serial, qty, date_created
FROM total
WHERE rownum = 1
Demo: SQL Fiddle
This will work as long as you don't have two records with the same ID created in the same second:
WITH RecentSUM AS
(
SELECT ID, MAX(DateCreated) DateCreated, SUM(Quantity) TotalQuantity
FROM [table]
GROUP BY ID
)
SELECT t.ID, t.Serial, r.TotalQuantity, r.DateCreated
FROM RecentSUM r
INNER JOIN [table] t ON t.ID = r.ID and t.DateCreated=r.DateCreated;