Partition Over issue in SQL - sql

I have a Order shipment table like below -
Order_ID
shipment_id
pkg_weight
1
101
5
1
101
5
1
101
5
1
102
3
1
102
3
I want the output table to look like below -
Order_ID
Distinct_shipment_id
total_pkg_weight
1
2
8
select
order_id
, count(distinct(shipment_id)
, avg(pkg_weight) over (partition by shipment_id)
from table1
group by order_id
but getting the below error -
column "pkg_weight" must appear in the GROUP BY clause or be used in
an aggregate function
Please help

Use a distinct select first, then aggregate:
SELECT Order_ID,
COUNT(DISTINCT shipment_id) AS Distinct_shipment_id,
SUM(pkg_weight) AS total_pkg_weight
FROM
(
SELECT DISTINCT Order_ID, shipment_id, pkg_weight
FROM table1
) t
GROUP BY Order_ID;

Related

SQL SELECT query conditional for multiple possible values

I have the following data:
id
customer_id
status
1
1
Shipped
2
1
In Progress
3
1
Cancelled
4
2
Shipped
5
2
In Progress
6
3
Shipped
How do I do a SQL query to SELECT a row for each customer based on the status?
If the customer has a status of 'In Progress', then return only that in the results.
If the customer does not have a status of 'In Progress', but does have a status of 'Shipped', then return that instead.
So the results would be:
id
customer_id
status
2
1
In Progress
5
2
In Progress
6
3
Shipped
One option of tackling this problem is:
filtering out any status different than either 'Shipped' or 'In Progress', with a WHERE clause
using FIRST_VALUE, partitioned by "customer_id", ordered by "id" to get your last "id" and "status"
aggregating on duplicate records, using DISTINCT
SELECT DISTINCT
FIRST_VALUE(id) OVER(PARTITION BY customer_id ORDER BY id DESC) AS id,
customer_id,
FIRST_VALUE(status_) OVER(PARTITION BY customer_id ORDER BY id DESC) AS status_
FROM tab
WHERE status_ IN ('Shipped', 'In Progress')
This is likely to work on almost all the most common DBMS'.
A bit complex, as I need two common table expressions:
-- your input, don't use in final query
WITH
indata(id,customer_id,status) AS (
SELECT 1,1,'Shipped'
UNION ALL SELECT 2,1,'In Progress'
UNION ALL SELECT 3,1,'Cancelled'
UNION ALL SELECT 4,2,'Shipped'
UNION ALL SELECT 5,2,'In Progress'
UNION ALL SELECT 6,3,'Shipped'
)
-- real query starts here, replace following comma with "WITH" ...
,
w_rank AS (
SELECT
customer_id
, status
, CASE status
WHEN 'Shipped' THEN 1
WHEN 'In Progress' THEN 2
WHEN 'Cancelled' THEN 0
ELSE -1
END AS rnk
FROM indata
)
,
grp AS (
SELECT
customer_id
, MAX(rnk) AS rnk
FROM w_rank
GROUP BY
customer_id
)
SELECT
indata.id
, indata.customer_id
, indata.status
FROM grp
JOIN w_rank USING(customer_id,rnk)
JOIN indata USING(customer_id,status)
ORDER BY 1;
-- out id | customer_id | status
-- out ----+-------------+------------
-- out 2 | 1 | In Progress
-- out 5 | 2 | In Progress
-- out 6 | 3 | Shipped
Can be your DBMS does not support the USING() clause in joins - then use the ON clause.

Find gaps of a sequence in PostgreSQL tables

I have a table invoices with a field invoice_number. This is what happens when i execute select invoice_number from invoice
invoice_number
1
2
3
5
6
10
11
I want a SQL that gives me the following result:
gap_start
gap_end
1
3
5
6
10
11
demo:db<>fiddle
You can use row_number() window function to create a row count and use the difference to your actual values as group criterion:
SELECT
MIN(invoice) AS start,
MAX(invoice) AS end
FROM (
SELECT
*,
invoice - row_number() OVER (ORDER BY invoice) as group_id
FROM t
) s
GROUP BY group_id
ORDER BY start

How to build SQL to capture most unique value?

I am trying to build a query results with SQL. Here is my table:
CUST_ID ORDER_ID STORE_FREQUENCY
---------- ----------- ---------------
100 20122 500
100 20100 500
100 20100 737
200 20119 287
300 20130 434
300 20150 434
300 20130 434
300 20120 120
The expected output is:
CUST_ID UNIQUE_ORDERS TOP_STORE
--------- ----------------- ---------
100 2 737
200 1 287
300 3 434
The requirement for the output is:
TOP_STORE = Per CUST_ID, sort the STORE_FREQUENCY column by DESC and get the greatest store frequency
UNIQUE_ORDERS = Per CUST_ID, the number of unique ORDER_IDs in the column
I have started this SELECT statement, but having difficulties completing it to include the 2 columns correctly:
Select cust_id, Count(order_id) as unique_orders
From ORDERS_TABLE
Group By Order_ID
Can you help me complete the 2 columns?
Use aggregate functions such as COUNT(DISTINCT ...) and MAX()
SELECT CUST_ID, COUNT(DISTINCT ORDER_ID), MAX(STORE_FREQUENCY )
FROM TableName
GROUP BY CUST_ID
Here's a DEMO.
It seems to be that the top store should be the store with the greatest number of orders. If so, then CUST_ID 100 should have store 500 as the top store, not 737. In other words, I would expect the following output:
This requirement changes the query strategy, because we no longer can just do a single simple aggregation over the entire table. One approach is to do a separate calculation to find the top store for each customer, then join that result to a query similar to the other answers.
WITH cte AS (
SELECT CUST_ID, STORE_FREQUENCY, cnt,
ROW_NUMBER() OVER (PARTITION BY CUST_ID ORDER BY cnt DESC) rn
FROM
(
SELECT CUST_ID, STORE_FREQUENCY,
COUNT(*) OVER (PARTITION BY CUST_ID, STORE_FREQUENCY) cnt
FROM yourTable
) t
)
SELECT
t1.CUST_ID,
t1.UNIQUE_ORDERS,
t2.TOP_STORE
FROM
(
SELECT CUST_ID, COUNT(DISTINCT ORDER_ID) AS UNIQUE_ORDERS
FROM yourTable
GROUP BY CUST_ID
) t1
INNER JOIN
(
SELECT CUST_ID, STORE_FREQUENCY AS TOP_STORE
FROM cte
WHERE rn = 1
) t2
ON t1.CUST_ID = t2.CUST_ID;
Demo

sql fetch one record per date from table

I have a table like:
o_id order_no date etc....
4 1 2017-02-01
4 2 2017-02-01
4 3 2017-02-01
what i want is only one(highest order no) record per date should be fetched.
For example for only order_no 3 for o_id 4 should be fetched
Output:
o_id order_no date
4 3 2017-02-01
2 1 .........so on
The canonical method is to use the ANSI-standard ROW_NUMBER() function:
select t.*
from (select t.*,
row_number() over (partition by o_id order by order_no desc) as seqnum
from t
) t
where seqnum = 1;
You may use GROUP BY and MAX if the order_no is unique in each o_id group
select your_table.*
from your_table
join
(
select o_id, max(order_no) maxorder
from your_table
group by o_id
) t on t.o_id = your_table.oid and t.maxorder = your_table.order_no
Similar questions
Select corresponding to row from the same table - you can find there a performance comparison of group by and window function version, however, the comparison is for SQL Server.

SQL Server : how to select the rows in a table with the same value on a column but some exact values on another column for the grouped rows

I have this table with some sample data:
Supplier_ID Product_ID Stock
-----------------------------
1 272 1
1 123 5
1 567 3
1 564 3
2 272 4
2 12 3
2 532 1
3 123 4
3 272 5
I want to check the suppliers that have both products: 272 and 123, so the result would be like:
Supplier_ID
-----------
1
3
You can use GROUP BY and HAVING:
SELECT Supplier_ID
FROM your_tab
WHERE Product_ID IN (272, 123)
GROUP BY Supplier_ID
HAVING COUNT(DISTINCT Product_ID) = 2;
LiveDemo
Try this code:
SELECT A.Supplier_ID FROM
(SELECT Supplier_ID FROM Your_Table WHERE Product_ID = 272) AS A
INNER JOIN
(SELECT Supplier_ID FROM Your_Table WHERE Product_ID = 123) AS B
ON A.Supplier_ID = B.Supplier_ID
This is how it works using set operations. IMHO a too little used feature of databases.
select Supplier_ID from table1 where product_id=272
intersect
select Supplier_ID from table1 where product_id=123
it produces as well
Supplier_ID
1
3
By the way a distinct is not needed due to intersect delivers distinct rows.
http://sqlfiddle.com/#!6/13b11/3
Try This Code: By Using Row_number()
;WITH cte
AS (SELECT *,
Row_number()
OVER(
partition BY [Supplier_ID]
ORDER BY [Supplier_ID]) AS rn
FROM #Your_Table
WHERE Product_ID IN ( 272, 123 ))
SELECT DISTINCT Supplier_ID
FROM cte
WHERE rn > 1
OUTPUT:
Supplier_ID
1
3