HAVING COUNT() not giving out the expected result - sql

Attempting to get duplicate values of produce.prod_id using distinct, having clauses.but not giving out the expected result.
Here's my data:
PRODUCE
--------- -----------
PROD_ID PROD_NAME
--------- -----------
1 APPLES
2 PEARS
3 ORANGES
4 BANANAS
5 PEACHES
BUYERS
---------- ------------
BUYER_ID BUYER_NAME
---------- --------------
1 ADAM BARR
2 SEAN CHAI
3 EVA CORETS
4 ERIN O`MELIA
SALES
---------- --------- ------
BUYER_ID PROD_ID QTY
---------- --------- ------
1 2 15
1 3 5
4 1 37
3 5 11
4 3 1005
and here's my code:
select produce.prod_name,
buyer.BUYER_NAME,
SALES.PROD_ID
from produce
inner join SALES on produce.PROD_ID = SALES.PROD_ID
inner join buyer on SALES.BUYER_ID = buyer.BUYER_ID
group by produce.prod_name,
buyer.BUYER_NAME,
SALES.PROD_ID
having count(SALES.PROD_ID) > 1;
Expected result:
PROD_Name Buyer_Name
----------- --------------
Oranges ADAM BARR
Oranges ERIN O`MELIA

You need to use analytical function for your requirement
WITH PRODUCE(PROD_ID, PROD_NAME) AS (
SELECT 1, 'APPLES' from dual union all
select 2, 'PEARS' from dual union all
select 3, 'ORANGES' from dual union all
select 4, 'BANANAS' from dual union all
select 5, 'PEACHES' from dual),
BUYERS (BUYER_ID, BUYER_NAME) as (
select 1, 'ADAM BARR' from dual union all
select 2, 'SEAN CHAI' from dual union all
select 3, 'EVA CORETS' from dual union all
select 4, 'ERIN O`MELIA' from dual),
SALES(BUYER_ID, PROD_ID, QTY) as (
select 1, 2, 15 from dual union all
select 1, 3, 5 from dual union all
select 4, 1, 37 from dual union all
select 3, 5, 11 from dual union all
select 4, 3, 1005 from dual),
-- End of data preparation
TABLE_ AS (
SELECT produce.prod_name,
buyers.buyer_name,
sales.prod_id,
COUNT(1) OVER (PARTITION BY sales.prod_id) p_count
FROM produce
INNER JOIN sales
ON produce.prod_id = sales.prod_id
INNER JOIN buyers
ON sales.buyer_id = buyers.buyer_id)
SELECT prod_name, buyer_name, prod_id
FROM table_
WHERE p_count > 1;
Output:
| PROD_NAME | BUYER_NAME | PROD_ID |
|-----------|--------------|---------|
| ORANGES | ERIN O`MELIA | 3 |
| ORANGES | ADAM BARR | 3 |
Update: You simplified query would be:
With TABLE_ AS (
SELECT produce.prod_name,
buyers.buyer_name,
sales.prod_id,
COUNT(1) OVER (PARTITION BY sales.prod_id) p_count
FROM produce
INNER JOIN sales
ON produce.prod_id = sales.prod_id
INNER JOIN buyers
ON sales.buyer_id = buyers.buyer_id)
SELECT prod_name, buyer_name, prod_id
FROM table_
WHERE p_count > 1;

you don't need distinct. you only need group by and having. you also cant group by buyer_name to get a count > 1. that is where the issue lies, you can to do this as a set of nested queries
select dupes.prod_name,
buyers.buyer_name
from (
select produce.prod_name,
SALES.PROD_ID,
count(SALES.PROD_ID)
from produce
inner join SALES on produce.PROD_ID=SALES.PROD_ID
group by produce.prod_name,
SALES.PROD_ID
having count(*)>1
) as dupes
inner join sales on sales.prod_id = dupes.prod_id
inner join buyers on buyers.buyer_id = sales.buyer_id

Here's my, hopefully, simple answer.
Here's my SQL Fiddle scratch pad as well. http://sqlfiddle.com/#!9/d80f6/3
select P.Prod_Name, Buyer_Name
from Sales S
join
(select prod_id, count(prod_id) as ActSales
from Sales group by prod_id having count(prod_id) > 1) SQ
on S.Prod_Id = SQ.Prod_Id
join Produce P
on S.Prod_Id = P.Prod_Id
join Buyers B
on S.Buyer_Id = B.Buyer_Id

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

Join a table that depends on another table

I have a POST table, a CATEGORY table, a TAG table and a MIGTATION_TAG table, I explain the MIGTATION_TAG table contains the movement of the tags between the categories, for example the tag whose ID = 1 belongs to the category whose l 'ID = 10 if I change its category to 12 a line will be added to the MIGTATION_TAG table as follows:
ID 1 TAG_ID 1 CATEGOTY_ID 12
the POST table
id title content tag_id
---------- ---------- ---------- ----------
1 title1 Text... 1
2 title2 Text... 3
3 title3 Text... 1
4 title4 Text... 2
5 title5 Text... 5
6 title6 Text... 4
the CATEGORY table
id name
---------- ----------
1 category_1
2 category_2
3 category_3
the TAG table
id name fist_category_id
---------- ---------- ----------------
1 tag_1 1
2 tag_2 1
3 tag_3 3
4 tag_4 1
5 tag_5 2
the MIGTATION_TAG table
id tag_id category_id
---------- ---------- ----------------
9 1 3
8 5 1
7 1 2
5 3 1
4 2 2
3 5 3
2 3 3
1 1 3
so i would like to know how many posts are registered for each category.
in some cases if there has been no change of category for a tag then it keeps its first category,
I manage to join the TAG table to the POST table via LEFT JOIN but the problem is that the join must depend on the MIGTATION_TAG table which must check if there has been a migration, if so then it must bring me back the last MAX (tag_id ) for each tag ,
here is my query
select category, COUNT(*) AS numer_of_posts
from(
select CATEGORY.name,
case
when POST.tag_id is not null then CATEGORY.name
end as category
from POST
left join TAG ON POST.tag_id = TAG.id
left join (
select id, MAX(tag_id) tag_id
from MIGTATION_TAG
group by id, tag_id
) MIGTATION_TAG
ON TAG.id = MIGTATION_TAG.tag_id
left join CATEGORY on MIGTATION_TAG.category_id = CATEGORY.id
)
GROUP BY category
;
here is the result i want to display with my query
Important ! for the post with id = 6 the tag_id = 4 whish was not changed so it will be using the fist_category_id in TAG table
category numer_of_posts
---------- --------------
category_1 3
category_2 1
category_3 2
Best regards
You can use:
SELECT MAX(c.name) AS category,
COUNT(*)
FROM post p
INNER JOIN tag t
ON (p.tag_id = t.id)
LEFT OUTER JOIN (
SELECT tag_id,
MAX(category_id) KEEP (DENSE_RANK LAST ORDER BY id) AS category_id
FROM migration_tag
GROUP BY tag_id
) m
ON (t.id = m.tag_id)
INNER JOIN category c
ON ( COALESCE(m.category_id, t.first_category_id) = c.id )
GROUP BY c.id
ORDER BY category
Which, for the sample data:
CREATE TABLE POST ( id, title, content, tag_id ) AS
SELECT 1, 'title1', 'Text...', 1 FROM DUAL UNION ALL
SELECT 2, 'title2', 'Text...', 3 FROM DUAL UNION ALL
SELECT 3, 'title3', 'Text...', 1 FROM DUAL UNION ALL
SELECT 4, 'title4', 'Text...', 2 FROM DUAL UNION ALL
SELECT 5, 'title5', 'Text...', 5 FROM DUAL UNION ALL
SELECT 6, 'title6', 'Text...', 4 FROM DUAL;
CREATE TABLE CATEGORY ( id, name ) AS
SELECT 1, 'category_1' FROM DUAL UNION ALL
SELECT 2, 'category_2' FROM DUAL UNION ALL
SELECT 3, 'category_3' FROM DUAL;
CREATE TABLE TAG (id, name, first_category_id) AS
SELECT 1, 'tag_1', 1 FROM DUAL UNION ALL
SELECT 2, 'tag_2', 1 FROM DUAL UNION ALL
SELECT 3, 'tag_3', 3 FROM DUAL UNION ALL
SELECT 4, 'tag_4', 1 FROM DUAL UNION ALL
SELECT 5, 'tag_5', 2 FROM DUAL;
CREATE TABLE MIGRATION_TAG ( id, tag_id, category_id ) AS
SELECT 9, 1, 3 FROM DUAL UNION ALL
SELECT 8, 5, 1 FROM DUAL UNION ALL
SELECT 7, 1, 2 FROM DUAL UNION ALL
SELECT 5, 3, 1 FROM DUAL UNION ALL
SELECT 4, 2, 2 FROM DUAL UNION ALL
SELECT 3, 5, 3 FROM DUAL UNION ALL
SELECT 2, 3, 3 FROM DUAL UNION ALL
SELECT 1, 1, 3 FROM DUAL;
Outputs:
CATEGORY
COUNT(*)
category_1
3
category_2
1
category_3
2
fiddle
One option uses a left join to bring the tag table, and the a lateral join to lookup the latest migration, ifi any. We can then use conditional logic:
select coalesce(t2.category_id, t.first_category_id) category, count(*) number_of_posts
from post p
inner join tag t on t.id = p.tag_id
outer apply (
select mt.category_id
from migration_tag mt
where mt.tag_id = p.tag_id
order by mt.id desc fetch first row only
) t2
group by coalesce(t2.category_id, t.first_category_id)

Subqueries with Group By

I have looked around on the internet for a while for a way to make this query work but am unable to work it out so far. I am trying to return the item descriptions and the quantity of items sold, This is what I have got at the moment:
SELECT itemdesc, quantity, (SELECT COUNT(quantity) FROM invoiceitem
WHERE invoiceitem.itemno = item.itemno
GROUP BY COUNT(invoiceitem.quantity)) Quantity
FROM item;
I am very lost at the moment, not sure if I am even linking the right tables together, can provide an ER Diagram if it helps, any help would be greatly appreciated, thankyou.
ANSWER:
SELECT item.itemdesc, (SELECT SUM(invoiceitem.quantity) FROM invoiceitem
WHERE invoiceitem.itemno = item.itemno
GROUP BY item.itemdesc) Quantity
FROM item
ORDER BY quantity;
Thankyou all!
Your outer query:
SELECT itemdesc,
quantity
/* ignoring the subquery */
FROM item;
Will not work as the item table does not have a quantity column.
If you intended to use the itemprice column then your query would be:
SELECT itemdesc,
itemprice,
( SELECT COUNT(quantity)
FROM invoiceitem
WHERE invoiceitem.itemno = item.itemno
) AS Quantity
FROM item
Which, for the sample data:
CREATE TABLE item (
itemno PRIMARY KEY,
itemdesc,
itemprice
) AS
SELECT 1, 'ItemA', 1 FROM DUAL UNION ALL
SELECT 2, 'ItemB', 2 FROM DUAL UNION ALL
SELECT 3, 'ItemC', 3 FROM DUAL UNION ALL
SELECT 4, 'ItemD', 4 FROM DUAL UNION ALL
SELECT 5, 'ItemE', 5 FROM DUAL;
CREATE TABLE invoiceitem( itemno, quantity ) AS
SELECT 1, 10 FROM DUAL UNION ALL
SELECT 1, 20 FROM DUAL UNION ALL
SELECT 1, 30 FROM DUAL UNION ALL
SELECT 3, 40 FROM DUAL UNION ALL
SELECT 4, 50 FROM DUAL UNION ALL
SELECT 5, NULL FROM DUAL;
Outputs:
ITEMDESC | ITEMPRICE | QUANTITY
:------- | --------: | -------:
ItemA | 1 | 3
ItemB | 2 | 0
ItemC | 3 | 1
ItemD | 4 | 1
ItemE | 5 | 0
And equivalent query using a join would be (assuming that item.itemno is a primary key):
SELECT MAX( i.itemdesc ) AS itemdesc,
MAX( i.itemprice ) AS itemprice,
COUNT(ii.quantity) AS Quantity
FROM item i
LEFT OUTER JOIN invoiceitem ii
ON ( ii.itemno = i.itemno )
GROUP BY i.itemno
You need to use LEFT OUTER JOIN rather than INNER JOIN to join the corresponding rows with zero items in the invoiceitem table.
db<>fiddle here
Group by like below should work. Please check.
select
item.itemdesc, count(invoiceitem.quantity) Quantity
from
item item
join invoiceitem invoiceitem on item.itemno = invoiceitem.itemno
group by
item.itemdesc
Thanks

Oracle SQL - Assign queue of customers to list of Employees

I need to distribute list of employees to customers.
For example:
Table 1: List of Employees: A, B & C
Table 2: List of Customers: 1, 2, 3, 4, 5, 6, 7, 8, 9
The needed result:
|------------|------------|
| Customers | Employees |
|------------|------------|
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | A |
| 5 | B |
| 6 | C |
| 7 | A |
| 8 | B |
| 9 | C |
|------------|------------|
You can use ROW_NUMBER() to assign a number on the fly to the employees, and the MOD() to do the rolling join. For example:
select
c.id,
e.name
from (
select t.*,
row_number() over(order by name) as rn
from employees t
) e
join customers c on e.rn =
mod(rn, (select count(*) from customers)) + 1
Number all rows, then use a modulo function for the join:
with e as
(
select employee, row_number() over (order by employee) as rn
from employees
)
, c as
(
select customer, row_number() over (order by customer) as rn
from customers
)
select c.customer, e.employee
from c
join e on e.rn - 1 = mod(c.rn - 1, (select count(*) from e))
order by c.customer;
Demo: https://dbfiddle.uk/?rdbms=oracle_18&fiddle=43a6ca7469dff023d5513fa209e33ea7
You can use MOD function for this purpose. Try below code.
CREATE TABLE EMP
AS
SELECT 'A' AS EMP FROM DUAL
UNION ALL
SELECT 'B' AS EMP FROM DUAL
UNION ALL
SELECT 'C' AS EMP FROM DUAL;
CREATE TABLE CUST
AS
SELECT '1' AS CUST FROM DUAL
UNION ALL
SELECT '2' AS CUST FROM DUAL
UNION ALL
SELECT '3' AS CUST FROM DUAL
UNION ALL
SELECT '4' AS CUST FROM DUAL
UNION ALL
SELECT '5' AS CUST FROM DUAL
UNION ALL
SELECT '6' AS CUST FROM DUAL
UNION ALL
SELECT '7' AS CUST FROM DUAL
UNION ALL
SELECT '8' AS CUST FROM DUAL
UNION ALL
SELECT '9' AS CUST FROM DUAL;
SELECT CUST, EMP
FROM (SELECT ROW_NUMBER () OVER (PARTITION BY 1 ORDER BY EMP) AS ID, EMP
FROM EMP) EMP
INNER JOIN CUST ON MOD (TO_NUMBER (CUST.CUST) - 1, 3) = EMP.ID - 1
ORDER BY 1;

Select Duplicate records in Oracle

I have a table below in Oracle
Table1
State | Product |other fields
CA | P1 | xxxx
OR | P1 | xxxx
OR | P1 | xxxx
OR | P1 | xxxx
WA | P1 | xxxx
VA | P2 | xxxx
My Output should be only select if State has been occurred more than once.
State | Product |other fields
OR | P1 | xxxx
If you just want to count duplicate states then:
SELECT DISTINCT
State,
Product,
Other_Fields
FROM (
SELECT t.*,
COUNT(1) OVER ( PARTITION BY State ) AS cnt
FROM Table1 t
)
WHERE cnt > 1;
If you want to consider duplicate rows (considering all fields) then:
SELECT *
FROM Table1
GROUP BY State, Product, Other_Fields
HAVING COUNT(1) > 1;
select state, product, column_3, column_4
from (
select state, product, column_3, column_4,
count(*) over (partition by state) as cnt
from the_table
) t
where cnt > 1;
You could use ROW_NUMBER analytic function.
For example,
SQL> WITH sample_data AS(
2 SELECT 'CA' State, 'P1' product FROM dual UNION ALL
3 SELECT 'OR', 'P1' product from dual union all
4 SELECT 'OR', 'P1' product FROM dual UNION ALL
5 SELECT 'OR', 'P1' product FROM dual UNION ALL
6 SELECT 'WA', 'P1' product FROM dual UNION ALL
7 SELECT 'VA', 'P2' product from dual
8 )
9 -- end of sample_data mimicking real table
10 SELECT distinct state,
11 product
12 FROM
13 (SELECT state,
14 product,
15 row_number() OVER(PARTITION BY state ORDER BY product) rn
16 FROM sample_data
17 )
18 WHERE rn >1;
ST PR
-- --
OR P1
SQL>