SQL Server Group By and First in priority - sql

I have a data table
SQL Fiddle
http://sqlfiddle.com/#!18/b33c86
Schema
create table lenderdata
(
ID int identity
primary key,
LinkID varchar(250) null,
Lender varchar(250) null,
Item varchar(250) null,
Priority int null,
Quantity int null,
Status varchar(250) null
);
INSERT INTO lenderdata (LinkID, Lender, Item, Priority, Quantity, Status) VALUES ('001', 'A', 'Apple', 1, 100, 'PENDING');
INSERT INTO lenderdata (LinkID, Lender, Item, Priority, Quantity, Status) VALUES ('001', 'B', 'Orange', 2, 100, 'PENDING');
INSERT INTO lenderdata (LinkID, Lender, Item, Priority, Quantity, Status) VALUES ('002', 'C', 'Strawberry', 1, 1000, 'PENDING');
INSERT INTO lenderdata (LinkID, Lender, Item, Priority, Quantity, Status) VALUES ('002', 'D', 'grapes', 2, 100, 'PENDING');
INSERT INTO lenderdata (LinkID, Lender, Item, Priority, Quantity, Status) VALUES ('003', 'E', 'coffee', 1, 1000, 'PROCESSING');
INSERT INTO lenderdata (LinkID, Lender, Item, Priority, Quantity, Status) VALUES ('003', 'F', 'mango', 2, 1000, 'PENDING');
I want to group by Link ID and pick up only the one with minimum of priority if the status is PENDING
If the group by data has other status e.g. PROCESSING then it should simply ignore that group
Only if all the status is either PENDING excluding the group if there is one with PROCESSING status
If I was to run the query it would only return Link ID 001 and 002

Your description is hard to make sense of but given your expectation perhaps this is what you require?
select LinkId
from lenderdata
group by LinkId
having Min(status) = 'Pending' and Max(Status) = 'Pending';

You could use the following where clause to filter out any LinkID group that has a status <> 'pending':
WHERE LinkID NOT IN
(
SELECT LinkID FROM lenderdata WHERE Status <> 'PENDING'
)
Now, according to your request: (I want to group by Link ID and pick up only the one with a minimum priority)
If you meant to select rows with a minimum priority for each LinkID group, then you may use the row_number function approach.
WITH CTE AS
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY LinkID ORDER BY Priority) rn
FROM lenderdata
WHERE LinkID NOT IN
(
SELECT LinkID FROM lenderdata WHERE Status <> 'PENDING'
)
)
SELECT ID, LinkID, Lender, Item, Priority, Quantity, Status
FROM CTE WHERE rn = 1
If you meant to select rows with a minimum priority among all LinkID groups, then you may use the rank function approach.
WITH CTE AS
(
SELECT *,
RANK() OVER (ORDER BY Priority) rnk
FROM lenderdata
WHERE LinkID NOT IN
(
SELECT LinkID FROM lenderdata WHERE Status <> 'PENDING'
)
)
SELECT ID, LinkID, Lender, Item, Priority, Quantity, Status
FROM CTE WHERE rnk = 1
See a demo for your sample data.
See a demo for modified sample data to see the difference between the two approaches (your sample data will get you the same results for both approaches).

Related

How to fetch only the latest ordered items from the list by eliminating duplicate items SQL

How to write SQL query for fetch only the latest ordered items and remove the duplicated items. here BOOK Ordered on 2 days so fetch only latest ordered recorded.
expected result
so here two rows removed and pick the latest ordered items . PEN and BOOK ordered two times but only took the latest order.
Here is full working example:
CREATE TABLE example_table (
ID NUMBER PRIMARY KEY,
ITEM_ID NUMBER,
ORDER_ID NUMBER,
NAME VARCHAR2(50),
"Date" DATE
);
INSERT INTO example_table (ID, ITEM_ID, ORDER_ID, NAME, "Date") VALUES (101, 205, 301, 'CAP', to_date('12-12-2022','dd-mm-yyyy'));
INSERT INTO example_table (ID, ITEM_ID, ORDER_ID, NAME, "Date") VALUES (102, 201, 303, 'BOOK', to_date('01-01-2023','dd-mm-yyyy'));
INSERT INTO example_table (ID, ITEM_ID, ORDER_ID, NAME, "Date") VALUES (103, 202, 303, 'PEN', to_date('01-01-2023','dd-mm-yyyy'));
INSERT INTO example_table (ID, ITEM_ID, ORDER_ID, NAME, "Date") VALUES (104, 201, 304, 'BOOK', to_date('01-05-2023','dd-mm-yyyy'));
INSERT INTO example_table (ID, ITEM_ID, ORDER_ID, NAME, "Date") VALUES (105, 205, 304, 'BAG', to_date('01-05-2023','dd-mm-yyyy'));
INSERT INTO example_table (ID, ITEM_ID, ORDER_ID, NAME, "Date") VALUES (106, 202, 305, 'PEN', to_date('01-07-2023','dd-mm-yyyy'));
SELECT ID, ITEM_ID, ORDER_ID, NAME, "Date"
FROM
(
SELECT ID, ITEM_ID, ORDER_ID, NAME, "Date"
,ROW_NUMBER() OVER (PARTITION BY NAME ORDER BY "Date" DESC) AS "RowID"
FROM example_table
) DS
WHERE "RowID" = 1
ORDER BY ID
Try this
select *
from( select *, ROW_NUMBER() OVER(PARTITION BY ITEM_ID ORDER BY DATE DESC) as rn
from have
) as a
where rn = 1
;

How to using oracle distinct and order siblings by at the same time?

the distinct broken the order siblings by,how can i use them at the same time?
such as
select distinct * from table xxx starts with ... connect by id=pid order siblings by field
here is the test sqls executed with different results
select distinct * from test_table start with pid is null connect by prior id=pid order siblings by order_num;
select * from test_table start with pid is null connect by prior id=pid order siblings by order_num;
here is the table
create table TEST_TABLE(id NUMBER,pid NUMBER,order_num NUMBER);
insert into TEST_TABLE (id, pid, order_num) values (1, null, 1);
insert into TEST_TABLE (id, pid, order_num) values (2, 1, 5);
insert into TEST_TABLE (id, pid, order_num) values (3, null, 2);
insert into TEST_TABLE (id, pid, order_num) values (4, 1, 4);
insert into TEST_TABLE (id, pid, order_num) values (5, 3, 2);
You can use ORDER SIBLINGS BY in an inner query and then use ROW_NUMBER() analytic function to find the duplicates in an outer query and maintain the order using ORDER BY ROWNUM in that outer query:
SELECT id, pid, order_num
FROM (
SELECT id, pid, order_num,
ROW_NUMBER() OVER (PARTITION BY id, pid, order_num ORDER BY ROWNUM) AS rn
FROM (
SELECT id, pid, order_num
FROM test_table
START WITH pid IS NULL
CONNECT BY PRIOR id = pid
ORDER SIBLINGS BY order_num
)
ORDER BY ROWNUM
)
WHERE rn = 1
Which, for the sample data:
create table TEST_TABLE(id,pid,order_num) AS
SELECT 1, NULL, 1 FROM DUAL UNION ALL
SELECT 2, 1, 5 FROM DUAL UNION ALL
SELECT 3, NULL, 2 FROM DUAL UNION ALL
SELECT 4, 1, 4 FROM DUAL UNION ALL
SELECT 5, 3, 2 FROM DUAL UNION ALL
SELECT 1, 5, 5 FROM DUAL;
Note: this has an added row so there is a path back to a previous branch in the hierarchy to create duplicate rows in the output.
Outputs:
ID
PID
ORDER_NUM
1
null
1
4
1
4
2
1
5
3
null
2
5
3
2
1
5
5
and maintains the order of the siblings from the hierarchical query.
db<>fiddle here

Using UNION ALL on 2 tables to obtain specific results

I am attempting to query unique results but am having issues with Query 2 as it pulls the Top 1 result of any ItemNumber instead of my desired ItemNumber from my select statement. I want to display all unique results from Query 1 & I want to display only 1 unique result from Query 2 where NULL Locations are prioritized. (1 'Y' SpecialItem associated per ItemNumber. I have mocked up a small example of my issue to keep the question from being too complex.
Table being used:
CREATE TABLE QA_TESTING (
ItemNumber varchar(255),
ItemName varchar(255),
Location varchar(255)
)
Data being used:
Insert into QA_Testing (ItemNumber, ItemName, Location) Values ('333', 'Apple', 'USA')
Insert into QA_Testing (ItemNumber, ItemName, Location) Values ('501', 'Apple', NULL)
Insert into QA_Testing (ItemNumber, ItemName, Location) Values ('501', 'Apple', NULL)
Insert into QA_Testing (ItemNumber, ItemName, Location) Values ('501', 'Apple', 'USA')
Insert into QA_Testing (ItemNumber, ItemName, Location) Values ('501', 'Apple', NULL)
Insert into QA_Testing (ItemNumber, ItemName, Location) Values ('405', 'Apple', 'USA')
Insert into QA_Testing (ItemNumber, ItemName, Location) Values ('405', 'Orange', 'USA')
Insert into QA_Testing (ItemNumber, ItemName, Location) Values ('501', 'Apple', 'USA')
My View:
IF EXISTS (SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.VIEWS
WHERE TABLE_NAME = N'QA_TESTING_VW')
DROP VIEW QA_TESTING_VW
GO
CREATE VIEW dbo.QA_TESTING_VW
AS
(
(
--Query 1
Select DISTINCT QAT.ItemNumber, QAT.ItemName, Null AS SpecialItem, QAT.Location
From QA_Testing QAT
Where ItemName = 'Apple'
)
UNION ALL
(
--I Want to prioritize to show NULL over a non-null location in second query. If there is a null, show the null, otherwise, show the populated location. Only 1 row should be returned.
--Query 2
Select DISTINCT TOP 1 QAT.ItemNumber, QAT.ItemName, 'Y' AS SpecialItem, QAT.Location
From QA_Testing QAT
Where ItemName = 'Apple'
--ORDER BY Location ASC --ORDER BY TAKES TOO LONG
)
)
GO
My Select Statement:
--This has to stay in a simple format like so, with no additional unions, joins, etc.
Select * from QA_TESTING_VW where ItemNumber in ('501','830')
Outcome:
Expected Outcome:
there is comment "ORDER BY TAKES TOO LONG"
that's why I'm not quite sure.
As fuel for thought:
Select distinct QAT.ItemNumber, QAT.ItemName, O.SpecialItem, QAT.[Location]
From
(
Select QAT.ItemNumber
,QAT.ItemName
,QAT.[Location]
,row_number() over(partition by QAT.ItemNumber, QAT.ItemName order by QAT.[Location]) as LocationPriority
From QA_Testing QAT
Where ItemName = 'Apple'
) QAT
left join
(
select 'Y' AS SpecialItem
union all
select null
) O
on QAT.LocationPriority = 1

Incorrect syntax near the keyword 'select'. employees details with highest purchase value

Code to create tables for customer name and order details:
CREATE TABLE customers_demo_1 (
id INTEGER NOT NULL,
name TEXT,
email TEXT);
INSERT INTO customers_demo_1 (id, name, email) VALUES (1, 'Doctor Who', 'doctorwho#timelords.com');
INSERT INTO customers_demo_1 (id, name, email) VALUES (2, 'Harry Potter', 'harry#potter.com');
INSERT INTO customers_demo_1 (id, name, email) VALUES (3, 'Captain Awesome', 'captain#awesome.com');
CREATE TABLE orders_demo_1 (
id INTEGER NOT NULL,
customer_id INTEGER,
item TEXT,
price REAL);
INSERT INTO orders_demo_1 (id, customer_id, item, price)
VALUES (111, 1, 'Sonic Screwdriver', 1000.00);
INSERT INTO orders_demo_1 (id,customer_id, item, price)
VALUES (211,2, 'High Quality Broomstick', 40.00);
INSERT INTO orders_demo_1 (id,customer_id, item, price)
VALUES (311,3, 'TARDIS', 1000000.00);
INSERT INTO orders_demo_1 (id, customer_id, item, price)
VALUES (111, 1, 'Item2', 1300.00);
INSERT INTO orders_demo_1 (id,customer_id, item, price)
VALUES (112, 1, 'Item3', 400.00);
INSERT INTO orders_demo_1 (id,customer_id, item, price)
VALUES (111,1, 'Item7', 1900000.00);
INSERT INTO orders_demo_1 (id,customer_id, item, price)
VALUES (311,3, 'Item5', 1200.00);
INSERT INTO orders_demo_1 (id,customer_id, item, price)
VALUES (312,3, 'Item4', 1990.00);
INSERT INTO orders_demo_1 (id,customer_id, item, price)
VALUES (211,2, 'Item9', 2300.00);
INSERT INTO orders_demo_1 (id,customer_id, item, price)
VALUES (212,2, 'Item10', 2400.00);
INSERT INTO orders_demo_1 (id,customer_id, item, price)
VALUES (211,2, 'Item11', 5000.00);
INSERT INTO orders_demo_1 (id,customer_id, item, price)
VALUES (212,2, 'Item12', 8000.00);
Code:
select * from(
select customer_id, id,
sum(price) as Order_price_overall,
RANK() over (partition by customer_id order by sum(price) desc) as rank_order_value,
count(*) over(partition by customer_id) as no_of_orders
from orders_demo_1 o
group by id, customer_id
) t
inner join
select name,email from customers_demo_1 c
on t.customer_id=c.id
where t.rank_order_value=1
I want the highest sales value per customer to be displayed.
Error:
Incorrect syntax near the keyword 'select'.
Msg 156, Level 15, State 1, Line 63
Incorrect syntax near the keyword 'on'.
Expected output
customer_id Name id Order_price_overall Rank_order_value no_of_orders
1 Doctor who 111 1902300 1 2
2 Harry Potter 212 10400 1 2
3 Captain Awesome 311 1001200 1 2
Corrected the syntax
SELECT *
FROM (
SELECT customer_id
,id
,sum(price) AS Order_price_overall
,RANK() OVER (
PARTITION BY customer_id ORDER BY sum(price) DESC
) AS rank_order_value
,count(*) OVER (PARTITION BY customer_id) AS no_of_orders
FROM orders_demo_1 o
GROUP BY id
,customer_id
) t
INNER JOIN (
SELECT id
,name
,email
FROM customers_demo_1
) c ON t.customer_id = c.id
WHERE t.rank_order_value = 1
This works for me:
select t.customer_id, t.Order_price_overall,t.no_of_orders,t.id as OrderId, t.rank_order_value, c.name, c.email from(
select customer_id, id,
sum(price) as Order_price_overall,
RANK() over (partition by customer_id order by sum(price) desc) as rank_order_value,
count(*) over(partition by customer_id) as no_of_orders
from orders_demo_1 o
group by id, customer_id
) t
inner join
(select * from customers_demo_1) c
on t.customer_id=c.id
where t.rank_order_value=1

Select the first record of the last group when there are repeating groups

Trying to select the first record of the latest repeating STATUS groups for each POLICY_ID. How can I do this?
Edit/Note: There can be more than two status repetitions as shown in the last three rows.
View of the data:
Desired output:
SQL for data:
--drop table mytable;
create table mytable (ROW_ID Number(5), POLICY_ID Number(5),
CHANGE_NO Number(5), STATUS VARCHAR(50), CHANGE_DATE DATE);
insert into mytable values ( 81, 1, 1, 'A', date '2018-01-01');
insert into mytable values ( 95, 1, 2, 'A', date '2018-01-02');
insert into mytable values ( 100, 1, 3, 'B', date '2018-01-03');
insert into mytable values ( 150, 1, 4, 'C', date '2018-01-04');
insert into mytable values ( 165, 1, 5, 'A', date '2018-01-05');
insert into mytable values ( 175, 1, 6, 'A', date '2018-01-06');
insert into mytable values ( 599, 2, 1, 'S', date '2018-01-11');
insert into mytable values ( 602, 2, 2, 'S', date '2018-01-12');
insert into mytable values ( 611, 2, 3, 'S', date '2018-01-13');
insert into mytable values ( 629, 2, 4, 'T', date '2018-01-14');
insert into mytable values ( 720, 2, 5, 'U', date '2018-01-15');
insert into mytable values ( 790, 2, 6, 'S', date '2018-01-16');
insert into mytable values ( 812, 2, 7, 'S', date '2018-01-17');
insert into mytable values ( 825, 2, 8, 'S', date '2018-01-18');
select * from mytable;
Hmmm . . .
select t.*
from (select t.*,
row_number() over (partition by policy_id order by change_date asc) as seqnum
from t
where not exists (select 1
from t t2
where t2.policy_id = t.policy_id and
t2.status <> t.status and
t2.change_date > t.change_date
)
) t
where seqnum = 1;
The inner subquery finds all rows where -- for a given policy number -- there is no later row with a different status. That defines the last group of records.
It then uses row_number() to enumerate the rows. These outer query selects the first row for each policy_number.
You can use LEAD and LAG functions to identify the rows that begin a "repetition". The condition status <> previous status and status = next status will identify such rows.
SELECT *
FROM (
SELECT cte1.*, ROW_NUMBER() OVER (PARTITION BY POLICY_ID ORDER BY CHANGE_DATE DESC) AS rn
FROM (
SELECT mytable.*, CASE WHEN
STATUS <> LAG(STATUS, 1, '!') OVER (PARTITION BY POLICY_ID ORDER BY CHANGE_DATE) AND
STATUS = LEAD(STATUS) OVER (PARTITION BY POLICY_ID ORDER BY CHANGE_DATE)
THEN 1 END AS toselect
FROM mytable
) cte1
WHERE toselect = 1
) cte2
WHERE rn = 1
If you are using Oracle 12c you could use MATCH_RECOGNIZE:
SELECT ROW_ID, POLICY_ID, CHANGE_NO, STATUS, CHANGE_DATE
FROM mytable
MATCH_RECOGNIZE (
PARTITION BY POLICY_ID
ORDER BY CHANGE_DATE
MEASURES MATCH_NUMBER() m,FIRST(R.ROW_ID) r
ALL ROWS PER MATCH
PATTERN (R+)
DEFINE R AS STATUS=NEXT(STATUS)
) MR
WHERE ROW_ID = R
ORDER BY ROW_NUMBER() OVER(PARTITION BY POLICY_ID ORDER BY M DESC)
FETCH FIRST 1 ROW WITH TIES;
db<>fiddle demo
Alternatively:
SELECT *
FROM mytable
MATCH_RECOGNIZE (
PARTITION BY POLICY_ID
ORDER BY CHANGE_DATE DESC
MEASURES MATCH_NUMBER() m
,LAST(R.ROW_ID) ROW_ID
,LAST(R.STATUS) STATUS
,LAST(R.CHANGE_NO) CHANGE_NO
,LAST(R.CHANGE_DATE) CHANGE_DATE
ONE ROW PER MATCH
PATTERN (R+)
DEFINE R AS STATUS=PREV(STATUS)
) MR
WHERE M = 1
db<>fiddle demo2
Another approach with match_recognize:
select row_id, policy_id, change_no, status, change_date
from mytable
match_recognize (
partition by policy_id
order by change_date
measures
strt.row_id as row_id
, strt.change_no as change_no
, strt.change_date as change_date
, strt.status as status
pattern (strt unchanged* final)
define
unchanged as next(unchanged.status) = prev(unchanged.status)
, final as next(final.status) is null
) mr
order by mr.policy_id;