Oracle SQL - order table - sql

I am new to Oracle SQL and have a table below.
TABLE NAME : ORDERS
CNUM AMT SNUM
1001 1000 2001
1002 2000 2002
1001 1500 2001
1001 500 2001
need to get only those data where cnum (customer number) is serviced by more than 3 snum(sales person) from this above table
thank you
Asit

Something like that?
select cnum, count(*), sum(amount)
from orders
group by cnum
having count(*) > 3
Not sure what you need - aggregate result or every single row. If you need every row then try this one:
select * from (
select a.*, count(*) over(partition by cnum) cnt
from orders a
)
where cnt > 3

You could use the analytic function ROW_NUMBER.
For example,
SQL> WITH DATA AS(
2 SELECT t.*, row_number() OVER(PARTITION BY cnum ORDER BY snum) rn FROM t
3 )
4 SELECT cnum, amt, snum FROM DATA
5 WHERE rn >=3;
CNUM AMT SNUM
---------- ---------- ----------
1001 1500 2001
SQL>
Based on your sample data, the above gives the result including 3 snum for cnum, if you want more than 3, then replace >= with >.

You can group and count distinct SNUMs:
select CNUM
from ORDERS
group by CNUM
having count(distinct SNUM) > 3

WITH tab
AS (SELECT 1001 CNUM, 1000 AMT, 2001 SNUM FROM DUAL
UNION ALL
SELECT 1002, 2000, 2002 FROM DUAL
UNION ALL
SELECT 1001, 1500, 2001 FROM DUAL
UNION ALL
SELECT 1001, 500, 2001 FROM DUAL)
SELECT cnum
FROM tab
GROUP BY cnum
HAVING COUNT (*) >= 3;
If you only need the cnum and the snum you could do the following witch is partially taken from Rusty's answer:
WITH tab
AS (SELECT 1001 CNUM, 1000 AMT, 2001 SNUM FROM DUAL
UNION ALL
SELECT 1002, 2000, 2002 FROM DUAL
UNION ALL
SELECT 1001, 1500, 2001 FROM DUAL
UNION ALL
SELECT 1001, 500, 2001 FROM DUAL)
SELECT DISTINCT cnum, snum
FROM (SELECT cnum, snum, COUNT (*) OVER (PARTITION BY cnum) RANK FROM tab)
WHERE RANK >= 3;

Related

get item with GREATEST amount of purchases show all customers

I have a query, which finds the GREATEST amount of which item was purchased. It appears to be working fine (see below).
I have two questions, first is there a better way to rewrite this query without using 2 CTE and not displaying the value of rnk in the output.
Secondly, in another query
how can I show the product_id, product_name, price,
customer_id, first_name, last_name, total_qty, total_amt for this item. I am expecting to see 3 rows based on the data provided.
CREATE TABLE customers
(CUSTOMER_ID, FIRST_NAME, LAST_NAME) AS
SELECT 1, 'Faith', 'Mazzarone' FROM DUAL UNION ALL
SELECT 2, 'Lisa', 'Saladino' FROM DUAL UNION ALL
SELECT 3, 'Micheal', 'Palmice' FROM DUAL UNION ALL
SELECT 4, 'Jerry', 'Torchiano' FROM DUAL;
CREATE TABLE items
(PRODUCT_ID, PRODUCT_NAME, PRICE) AS
SELECT 100, 'Black Shoes', 79.99 FROM DUAL UNION ALL
SELECT 101, 'Brown Pants', 111.99 FROM DUAL UNION ALL
SELECT 102, 'White Shirt', 10.99 FROM DUAL;
CREATE TABLE purchases(
ORDER_ID NUMBER GENERATED BY DEFAULT AS IDENTITY (START WITH 1) NOT NULL,
CUSTOMER_ID NUMBER,
PRODUCT_ID NUMBER,
QUANTITY NUMBER,
PURCHASE_DATE TIMESTAMP
);
INSERT INTO purchases
(CUSTOMER_ID, PRODUCT_ID, QUANTITY, PURCHASE_DATE)
SELECT 1, 101, 3, TIMESTAMP'2022-10-11 09:54:48' FROM DUAL UNION ALL
SELECT 1, 100, 1, TIMESTAMP '2022-10-12 19:04:18' FROM DUAL UNION ALL
SELECT 2, 101,1, TIMESTAMP '2022-10-11 09:54:48' FROM DUAL UNION ALL
SELECT 2, 101, 3, TIMESTAMP '2022-10-17 19:34:58' FROM DUAL UNION ALL
SELECT 2, 102, 3,TIMESTAMP '2022-12-06 11:41:25' + NUMTODSINTERVAL ( LEVEL * 2, 'DAY') FROM dual CONNECT BY LEVEL <= 6 UNION ALL
SELECT 2, 102, 3,TIMESTAMP '2022-12-26 11:41:25' + NUMTODSINTERVAL ( LEVEL * 2, 'DAY') FROM dual CONNECT BY LEVEL <= 6 UNION ALL
SELECT 3, 101,1, TIMESTAMP '2022-12-21 09:54:48' FROM DUAL UNION ALL
SELECT 3, 102,1, TIMESTAMP '2022-12-27 19:04:18' FROM DUAL UNION ALL
SELECT 3, 102, 4,TIMESTAMP '2022-12-22 21:44:35' + NUMTODSINTERVAL ( LEVEL * 2, 'DAY') FROM dual
CONNECT BY LEVEL <= 15 UNION ALL
SELECT 3, 101,1, TIMESTAMP '2022-12-11 09:54:48' FROM DUAL UNION ALL
SELECT 3, 102,1, TIMESTAMP '2022-12-17 19:04:18' FROM DUAL UNION ALL
SELECT 3, 102, 4,TIMESTAMP '2022-12-12 21:44:35' + NUMTODSINTERVAL ( LEVEL * 2, 'DAY') FROM dual
CONNECT BY LEVEL <= 5;
ALTER TABLE customers
ADD CONSTRAINT customers_pk PRIMARY KEY (customer_id);
ALTER TABLE items
ADD CONSTRAINT items_pk PRIMARY KEY (product_id);
ALTER TABLE purchases
ADD CONSTRAINT order_pk PRIMARY KEY (order_id);
ALTER TABLE purchases ADD CONSTRAINT customers_fk FOREIGN KEY (customer_id) REFERENCES customers(customer_id);
ALTER TABLE purchases ADD CONSTRAINT items_fk FOREIGN KEY (PRODUCT_ID) REFERENCES items(product_id);
/*
get item with GREATEST amount of purchases
*/
with cte as
(SELECT
i.product_id,
i.product_name,
i.price,
SUM (p.quantity) AS total_qty,
SUM (p.quantity * i.price) AS total_amt
FROM purchases p
JOIN customers c ON p.customer_id = c.customer_id
JOIN items i ON p.product_id = i.product_id
GROUP BY
i.product_id,
i.product_name,
i.price, p.quantity),
cte2 as
(SELECT product_id,
product_name,
price,
total_qty,
total_amt,
RANK() OVER (ORDER BY total_amt DESC) rnk
FROM cte)
SELECT *
FROM cte2
WHERE rnk = 1;
PRODUCT_ID PRODUCT_NAME PRICE TOTAL_QTY TOTAL_AMT RNK
102 White Shirt 10.99 80 879.2 1
You could create a cte (or a subquery) that will give you all the data you need to get either greatest amount of all products or greatest per product:
SELECT
i.PRODUCT_ID, i.PRODUCT_NAME, i.PRICE,
c.CUSTOMER_ID, c.FIRST_NAME, c.LAST_NAME,
Sum(p.QUANTITY) "TOTAL_QTY", Sum(p.QUANTITY * i.PRICE) "TOTAL_AMT",
RANK() OVER (PARTITION BY i.PRODUCT_ID ORDER BY i.PRODUCT_ID, Sum(p.QUANTITY * i.PRICE) DESC) "PRODUCT_RNK",
RANK() OVER (ORDER BY Sum(p.QUANTITY * i.PRICE) DESC) "TOTAL_RNK"
FROM
items i
INNER JOIN
purchases p ON(p.PRODUCT_ID = i.PRODUCT_ID)
INNER JOIN
customers c ON(c.CUSTOMER_ID = p.CUSTOMER_ID)
GROUP BY
i.PRODUCT_ID, i.PRODUCT_NAME, i.PRICE,
c.CUSTOMER_ID, c.FIRST_NAME, c.LAST_NAME
ORDER BY
i.PRODUCT_ID, Sum(p.QUANTITY * i.PRICE) DESC
SubQuery or CTE Result:
PRODUCT_ID PRODUCT_NAME PRICE CUSTOMER_ID FIRST_NAME LAST_NAME TOTAL_QTY TOTAL_AMT PRODUCT_RNK TOTAL_RNK
---------- ------------ ---------- ----------- ---------- --------- ---------- ---------- ----------- ----------
100 Black Shoes 79.99 1 Faith Mazzarone 1 79.99 1 6
101 Brown Pants 111.99 2 Lisa Saladino 4 447.96 1 2
101 Brown Pants 111.99 1 Faith Mazzarone 3 335.97 2 4
101 Brown Pants 111.99 3 Micheal Palmice 2 223.98 3 5
102 White Shirt 10.99 3 Micheal Palmice 82 901.18 1 1
102 White Shirt 10.99 2 Lisa Saladino 36 395.64 2 3
with this dataset you can get the results like below:
-- if it is a CTE named cte:
SELECT * FROM cte WHERE PRODUCT_RNK = 1
PRODUCT_ID PRODUCT_NAME PRICE CUSTOMER_ID FIRST_NAME LAST_NAME TOTAL_QTY TOTAL_AMT PRODUCT_RNK TOTAL_RNK
---------- ------------ ---------- ----------- ---------- --------- ---------- ---------- ----------- ----------
100 Black Shoes 79.99 1 Faith Mazzarone 1 79.99 1 6
101 Brown Pants 111.99 2 Lisa Saladino 4 447.96 1 2
102 White Shirt 10.99 3 Micheal Palmice 82 901.18 1 1
OR
SELECT * FROM cte WHERE TOTAL_RNK = 1
PRODUCT_ID PRODUCT_NAME PRICE CUSTOMER_ID FIRST_NAME LAST_NAME TOTAL_QTY TOTAL_AMT PRODUCT_RNK TOTAL_RNK
---------- ------------ ---------- ----------- ---------- --------- ---------- ---------- ----------- ----------
102 White Shirt 10.99 3 Micheal Palmice 82 901.18 1 1

How to concatenate consecutive rows in oracle

am beginner to oracle. I tried following source code to solve and get as following O/P.But cant, please give some ideas to solve this.
id product sales
--- ------------- --------
1 Soaps 1200
2 Ice cream 2300
3 Cool drinks 2500
4 Tv 5000
5 Mobile 10000
6 Washing machine 20000```
```O/P
id product sales
--- ------------- --------
1 Soaps 1200
2 Ice cream+Cool drinks 4800
3 Tv+Mobile 15000
6 Washing machine 20000```
There must be a category and product mapping table.
Product with category must be mapped to resolve your issue.
Select min(t.id) as id,
Listagg(t.product, ' + ') within group (order by t.id) as product,
Sum(t.sales) as sales
From your_table t
Join mapping_table m
On (m.product = t.product)
Group by m.catrgory;
Cheers!!
You can use lead() analytic function :
with t1 as
(
select id,
concat(concat(product,'+'),lead(product) over (order by id)) as product,
sales + lead(sales) over (order by id) as sales
from tab -- your original table
), t2 as
(
select *
from t1
where id in (2,4)
union all
select *
from tab
where id in (1,6)
)
select *
from t2
order by id;
Demo
I thing, You need add column for group by. Try please:
WITH temp as (SELECT 1 id, 1 group_id, 'Soaps' str, 1200 as price FROM dual
UNION ALL
SELECT 2 id, 2, 'Ice cream', 2300 FROM dual
UNION ALL
SELECT 3 id, 2, 'Cool drinks', 2300 FROM dual
UNION ALL
SELECT 4 id, 3, 'Tv', 5000 FROM dual
UNION ALL
SELECT 5 id, 3, 'Mobile', 10000 FROM dual
UNION ALL
SELECT 6 id, 4, 'Washing machine', 20000 FROM dual)
SELECT group_id, LISTAGG(str, ', ')
WITHIN GROUP (ORDER BY group_id) "str",
sum(price) price
FROM temp
GROUP BY group_id
result:
1 Soaps 1200
2 Cool drinks, Ice cream 4600
3 Mobile, Tv 15000
4 Washing machine 20000

Analytic Function: ROW_NUMBER( )

I have a table "Invoice"
id integer Primary key
customer_id Integer
total Number (*,2)
The query is to display all customer_id, total and running serial number to each customer with alias name as 'SNO'. And the records should be displayed in ascending order based on the customer_id and then by SNO.
Hints:
Analytic Function: ROW_NUMBER( )
Analytic Clause: query_partition_clause and order_by_clause.
I wrote the below query:
Select customer_id,
total,
ROW_NUMBER( ) OVER (PARTITION BY customer_id ORDER BY customer_id ASC) AS "SNO"
from invoice;
But the result is failing. What is that I am missing. Also what is meant "the records should be displayed in ascending order based on the customer_id and then by SNO".
The result I am getting is as below:
CUSTOMER_ID TOTAL SNO
1 70000 1
2 250000 1
2 560000 2
3 200000 1
3 45000 2
4 475000 1
5 50000 1
5 10000 2
6 600000 1
6 90000 2
Expected result is :
CUSTOMER_ID TOTAL SNO
1 70000 1
2 250000 1
2 560000 2
3 45000 1
3 200000 2
4 475000 1
5 10000 1
5 50000 2
6 600000 1
6 90000 2
TOTAL Column data is not matching.
You're close, you probably need to order the row_number by id (assuming it's ascending based on time)
Select customer_id,
total,
ROW_NUMBER( ) OVER (PARTITION BY customer_id ORDER BY id ASC) AS "SNO"
from invoice
order by customer_id, "SNO" -- should be the default anyway (but there's no guarantee)
I have not found any order by clause in your query, another issue in which order you want to generate SNO ? by using id or total that will impact on your ordering
with cte as
(
select 1 cid, 70000 total from dual
union all
select 2, 250000 from dual
union all
select 2, 560000 from dual
union all
select 3, 200000 from dual
union all
select 3, 45000 from dual
union all
select 4, 475000 from dual
union all
select 5, 50000 from dual
union all
select 5, 10000 from dual
union all
select 6, 600000 from dual
union all
select 6, 90000 from dual
)Select cid,total,ROW_NUMBER( ) OVER (PARTITION BY cid ORDER BY total ) AS "SNO" from cte order by cid,SNO

How to Get Set of Second Highest Values?

Suppose I have the following table:
employee_id salary
34 100
22 49
19 49
29 30
17 22
And I want to return the set of employees with the second highest salaries (when there are ties), as follows:
employee_id salary
22 49
19 49
How would I do that?
Use DENSE_RANK:
SELECT employee_id, salary
FROM
(
SELECT employee_id, salary, DENSE_RANK() OVER (ORDER BY salary DESC) dr
FROM yourTable
) t
WHERE dr = 2;
You can use nested query.
Steps taken :
Get all salary values ( sort it and obtain 2nd highest value ) :
SELECT salary FROM employee GROUP BY 1 ORDER BY 1 DESC limit 1 OFFSET 1;
OR can be written as :
SELECT salary FROM employee GROUP BY employee_id ORDER BY employee_id DESC limit 1 OFFSET 1;
Now use the query within employee table
SELECT * FROM employee where salary=(SELECT salary FROM employee GROUP BY 1 ORDER BY 1 DESC limit 1 OFFSET 1);
this can be done also in using query below,
scenario 1: Output two records
WITH employee
AS (
SELECT 34 emp_id, 100 rate FROM DUAL
UNION
SELECT 22 emp_id, 49 rate FROM DUAL
UNION
SELECT 19 emp_id, 49 rate FROM DUAL
UNION
SELECT 29 emp_id, 30 rate FROM DUAL
UNION
SELECT 17 emp_id, 22 rate FROM DUAL),
emp_rate_cnt AS
(SELECT rownum rown, rate, same_rate_count
FROM (SELECT rate, count(1) same_rate_count
FROM employee
GROUP BY rate
ORDER BY rate DESC))
SELECT *
FROM employee a
WHERE exists (SELECT 1
FROM emp_rate_cnt b
WHERE b.rate = a.rate
AND b.rown = 2
AND b.same_rate_count > 1);
scenario 2: Output no records
WITH employee
AS (
SELECT 34 emp_id, 100 rate FROM DUAL
UNION
SELECT 22 emp_id, 49 rate FROM DUAL
UNION
SELECT 19 emp_id, 50 rate FROM DUAL
UNION
SELECT 29 emp_id, 30 rate FROM DUAL
UNION
SELECT 17 emp_id, 22 rate FROM DUAL),
emp_rate_cnt AS
(SELECT rownum rown, rate, same_rate_count
FROM (SELECT rate, count(1) same_rate_count
FROM employee
GROUP BY rate
ORDER BY rate DESC))
SELECT *
FROM employee a
WHERE exists (SELECT 1
FROM emp_rate_cnt b
WHERE b.rate = a.rate
AND b.rown = 2
AND b.same_rate_count > 1);
I hope this is the easiest of all. Use rownum as it is Oracle.
SELECT t.employee_id, t.salary
FROM
(
SELECT distinct employee_id, salary, rownum as row from
FROM yourTable order by salary desc
) t
WHERE t.row = 2;

single row multiple columns into one column using sql server

I have table like this
OrderNo Item_Description1 Rate1 Quantity1 Item_Description2 Rate2 Quantity2 Item_Description3 Rate3 Quantity3
-------- ------------------ ------ ---------- ------------------ ------ ---------- ------------------ ------ ----------
1001 Laptop 50000 8 Air Conditioner 20000 10 Television 25000 12
1002 Washing Machine 35000 10 Camera 4000 20 Speaker 1500 15
From this I need to create a temp table or table like this:
OrderNo Item_Description Rate Quantity
-------- ------------------ ------ ----------
1001 Laptop 50000 8
Air Conditioner 20000 10
Television 25000 12
1002 Washing Machine 35000 10
Camera 4000 20
Speaker 1500 15
Is there a way I can do this in SQL Server?
You can also use CROSS APPLY to unpivot the data:
select t.order_no,
c.item_description,
c.rate,
c.quantity
from yourtable t
cross apply
(
select item_description1, rate1, quantity1 union all
select item_description2, rate2, quantity2 union all
select item_description3, rate3, quantity3
) c (item_description, rate, quantity)
SELECT * FROM
(select ORDER_NO,ITEM_DESCRIPTION1,RATE1,QUANTITY1FROM TABLE
UNION
select ORDER_NO,ITEM_DESCRIPTION2,RATE2,QUANTITY2 FROM TABLE
UNION
select ORDER_NO,ITEM_DESCRIPTION3,RATE3,QUANTITY3 FROM TABLE)AS A ORDER BY ORDER_NO
Try this
SELECT t.*
FROM Table1
OUTER APPLY
(
VALUES
([OrderNo],item_description1, rate1, quantity1),
(NULL, item_description2, rate2, quantity2),
(NULL, item_description3, rate3, quantity3)
) t([OrdNo],item_description, rate, quantity)
SQL FIDDLE DEMO
Or use #bluefeet answer with NULL
SELECT c.[OrderNo],
c.item_description,
c.rate,
c.quantity
FROM Table1 t
CROSS APPLY
(
SELECT [OrderNo],item_description1, rate1, quantity1 UNION ALL
SELECT NULL, item_description2, rate2, quantity2 UNION ALL
SELECT NULL, item_description3, rate3, quantity3
) c ([OrderNo],item_description, rate, quantity)
SQL FIDDLE DEMO
hope to help you!
select t.* from
(
select order_No, Item_Description1 as Item_Desription, Rate1 as Rate
from Table
union
select order_No, Item_Description2 as Item_Desription, Rate2 as Rate
from Table
union
select order_No, Item_Description3 as Item_Desription, Rate3 as Rate
from Table
) as t
Order by t.order_No asc
this is my test
select t.* from
(select id, apple1 as apple, orange1 as orange
from Test
union all
select id, apple2 as apple, orange2 as orange
from Test
union all
select id, apple3 as apple, orange3 as orange
from Test) as t
order by t.id asc