SQL:How to get min Quantity? - sql

I got this problem. When i tried to summarize the min quatity of nations's products and it did not work.
I have 2 tables below
PRODUCT:
ID|NAME |NaID|Qty
-------------------
01|Fruit|JP |50
02|MEAT |AUS |10
03|MANGA|JP |80
04|BOOK |AUS |8
NATION:
NaID |NAME
-------------------
AUS |Australia
JP |Japan
I want my result like this:
ID|NAME |Name|minQty
-------------------
01|Fruit|JP |50
04|BOOK |AUS |8
and i used:
select p.id,p.name, p.NaID,n.name,min(P.Qty)as minQty
from Product p,Nation n
where p.NaID=n.NaID
group by p.id,p.name, p.NaID,n.name,p.Qty
and i got this (T_T):
ID|NAME |NaID|minQty
-------------------
01|Fruit|JP |50
02|MEAT |AUS |10
03|MANGA|JP |80
04|BOOK |AUS |8
Please,Could soneone help me? I am thinking that i am bad at SQL now.

SQL Server 2005 supports window functions, so you can do something like this:
select id,
name,
NaID,
name,
qty
from (
select p.id,
p.name,
p.NaID,
n.name,
min(P.Qty) over (partition by n.naid) as min_qty,
p.qty
from Product p
join Nation n on p.NaID=n.NaID
) t
where qty = min_qty;
If there is more than one nation with the same minimum value, you will get each of them. If you don't want that, you need to use row_number()
select id,
name,
NaID,
name,
qty
from (
select p.id,
p.name,
p.NaID,
n.name,
row_number() over (partition by n.naid order by p.qty) as rn,
p.qty
from Product p
join Nation n on p.NaID = n.NaID
) t
where rn = 1;
As your example output with only includes the NaID but not the nation's name you don't really need the the join between product and nation.
(There is no DBMS product called "SQL 2005". SQL is just a (standard) for a query language. The DBMS product you mean is called Microsoft SQL Server 2005. Or just SQL Server 2005).

In Oracle, you can use several techniques. You can use subqueries and analytic functions, but the most efficient one is to use aggregate functions MIN and FIRST.
Your tables:
SQL> create table nation (naid,name)
2 as
3 select 'AUS', 'Australia' from dual union all
4 select 'JP', 'Japan' from dual
5 /
Table created.
SQL> create table product (id,name,naid,qty)
2 as
3 select '01', 'Fruit', 'JP', 50 from dual union all
4 select '02', 'MEAT', 'AUS', 10 from dual union all
5 select '03', 'MANGA', 'JP', 80 from dual union all
6 select '04', 'BOOK', 'AUS', 8 from dual
7 /
Table created.
The query:
SQL> select max(p.id) keep (dense_rank first order by p.qty) id
2 , max(p.name) keep (dense_rank first order by p.qty) name
3 , p.naid "NaID"
4 , n.name "Nation"
5 , min(p.qty) "minQty"
6 from product p
7 inner join nation n on (p.naid = n.naid)
8 group by p.naid
9 , n.name
10 /
ID NAME NaID Nation minQty
-- ----- ---- --------- ----------
01 Fruit JP Japan 50
04 BOOK AUS Australia 8
2 rows selected.
Since you're not using Oracle, a less efficient query, but probably working in all RDBMS:
SQL> select p.id
2 , p.name
3 , p.naid
4 , n.name
5 , p.qty
6 from product p
7 inner join nation n on (p.naid = n.naid)
8 where ( p.naid, p.qty )
9 in
10 ( select p2.naid
11 , min(p2.qty)
12 from product p2
13 group by p2.naid
14 )
15 /
ID NAME NAID NAME QTY
-- ----- ---- --------- ----------
01 Fruit JP Japan 50
04 BOOK AUS Australia 8
2 rows selected.
Note that if you have several rows with the same minimum quantity per nation, all those rows will be returned, instead of just one as in the previous "Oracle"-query.

with cte as (
select *,
row_number() over (partition by Nation order by qty) as [rn]
from product
)
select * from cte where [rn] = 1

Related

Counting unique combinations of values across multiple columns regardless of order?

I have a table that looks a bit like this:
Customer_ID | Offer_1 | Offer_2 | Offer_3
------------|---------|---------|--------
111 | A01 | 001 | B01
222 | A01 | B01 | 001
333 | A02 | 001 | B01
I want to write a query to figure out how many unique combinations of offers there are in the table, regardless of what order the offers appear in.
So in the example above there are two unique combinations: customers 111 & 222 both have the same three offers so they count as one unique combination, and then customer 333 is the only customer to have the three orders that they have. So the desired output of the query would be 2.
For some additional context:
The customer_ID column is in integer format, and all the offer
columns are in varchar format.
There are 12 offer columns and over 3 million rows in the actual
table, with over 100 different values in the offer columns. I
simplified the example to better illustrate what I'm trying to do, but any solution needs to scale to this amount of
possible combinations.
I can concatenate all of the offer columns together and then run a count distinct statement on the result, but this doesn't account for customers who have the same unique combination of offers but ordered differently (like customers 111 & 222 in the example above).
Does anyone know how to solve this problem please?
Assuming the character / doesn't show up in any of the offer names, you can do:
select count(distinct offer_combo) as distinct_offers
from (
select listagg(offer, '/') within group (order by offer) as offer_combo
from (
select customer_id, offer_1 as offer from t
union all select customer_id, offer_2 from t
union all select customer_id, offer_3 from t
) x
group by customer_id
) y
Result:
DISTINCT_OFFERS
---------------
2
See running example at db<>fiddle.
One way to do it would be to union all the offers into one column, then use select distinct listagg... to get the combinations of offers. Try this:
with u as
(select Customer_ID, Offer_1 as Offer from table_name union all
select Customer_ID, Offer_2 as Offer from table_name union all
select Customer_ID, Offer_3 as Offer from table_name)
select distinct listagg(Offer, ',') within group(order by Offer) from u
group by Customer_ID
Fiddle
The solution without UNION ALLs. It should have better performance.
/*
WITH MYTAB (Customer_ID, Offer_1, Offer_2, Offer_3) AS
(
VALUES
(111, 'A01', '001', 'B01')
, (222, 'A01', 'B01', '001')
, (333, 'A02', '001', 'B01')
)
*/
SELECT COUNT (DISTINCT LIST)
FROM
(
SELECT LISTAGG (V.Offer, '|') WITHIN GROUP (ORDER BY V.Offer) LIST
FROM MYTAB T
CROSS JOIN TABLE (VALUES T.Offer_1, T.Offer_2, T.Offer_3) V (Offer)
GROUP BY T.CUSTOMER_ID
)

Count Distinct Window Function with Groupby

I have a table that contains a user's name, market, and purchase_id. I'm trying to use a window function in SnowSql without a subquery to count the distinct number of purchases a user bought as well as the total number of unique purchases for the market.
Initial Table
User
Market
Purchase_ID
John Smith
NYC
1
John Smith
NYC
2
Bob Miller
NYC
2
Bob Miller
NYC
4
Tim Wilson
NYC
3
The desired result would look like this:
User
Purchases
Unique Market Purchases
John Smith
2
4
Bob Miller
2
4
Tim Wilson
1
4
The query I've been attempting without a subquery looks like the below but receives an error with the groupby.
SELECT
user,
COUNT(DISTINCT purchase_id),
COUNT(DISTINCT purchase_id) OVER (partition by market)
FROM table
GROUP BY 1
Appreciate any assistance with this. Thanks!
This might work , you could wrangle to get into the format you're after but it produces the answer without subquery.
Uses the awesome GROUPING SETS which allows multiple group-by clauses in a single statement - the exact error you were hitting :-).
Awesome question!
SELECT
COUNT(DISTINCT PURCHASE_ID)
, USER_NAME
, MARKET
FROM
CTE
GROUP BY
GROUPING SETS (USER_NAME, MARKET);
Copy|Paste|Run
WITH CTE AS (SELECT 'JOHN SMITH' USER_NAME, 'NYC' MARKET, 1
PURCHASE_ID
UNION SELECT 'JOHN SMITH' USER_NAME, 'NYC' MARKET, 2 PURCHASE_ID
UNION SELECT 'BOB MILLER' USER_NAME, 'NYC' MARKET, 2 PURCHASE_ID
UNION SELECT 'BOB MILLER' USER_NAME, 'NYC' MARKET, 4 PURCHASE_ID
UNION SELECT 'TIM WILSON' USER_NAME, 'NYC' MARKET, 3 PURCHASE_ID)
SELECT
COUNT(DISTINCT PURCHASE_ID)
, USER_NAME
, MARKET
FROM
CTE
GROUP BY
GROUPING SETS (USER_NAME, MARKET);
I don't think you can do this simply as an aggregation. But you can get the answer like this:
SELECT user,
SUM( (seqnum = 1)::INT ) as purchases,
SUM(SUM( (seqnum = 1)::INT )) OVER (PARTITION BY market) as market_purchases
FROM (SELECT t.*,
ROW_NUMBER() OVER (PARTITION BY purchase_id ORDER BY purchase_id) as seqnum
FROM table t
) t
GROUP BY 1
DISTTNCT in a window function is not allowed, so you need to use the subquery
CREATE TABLE table1
(`User` varchar(10), `Market` varchar(3), `Purchase_ID` int)
;
INSERT INTO table1
(`User`, `Market`, `Purchase_ID`)
VALUES
('John Smith', 'NYC', 1),
('John Smith', 'NYC', 2),
('Bob Miller', 'NYC', 2),
('Bob Miller', 'NYC', 4),
('Tim Wilson', 'NYC', 3)
;
SELECT
user,
COUNT(DISTINCT purchase_id)
,MAX((SELECT COUNT(DISTINCT purchase_id) FROM table1 WHERE `Market` = t1.`Market` )) bymarkte
FROM table1 t1
GROUP BY 1
user | COUNT(DISTINCT purchase_id) | bymarkte
:--------- | --------------------------: | -------:
Bob Miller | 2 | 4
John Smith | 2 | 4
Tim Wilson | 1 | 4
SELECT
user,
COUNT(DISTINCT purchase_id)
,MAX(countr) bymarkte
FROM table1 t1
INNER JOIN (SELECT `Market`,COUNT(DISTINCT purchase_id) countr FROM table1 GROUP BY `Market` ) ta ON t1.`Market` = ta.`Market`
GROUP BY 1
user | COUNT(DISTINCT purchase_id) | bymarkte
:--------- | --------------------------: | -------:
Bob Miller | 2 | 4
John Smith | 2 | 4
Tim Wilson | 1 | 4
db<>fiddle here

Reconciliation Automation Query

I have one database and time to time i change some part of query as per requirement.
i want to keep record of results of both before and after result of these queries in one table and want to show queries which generate difference.
For Example,
Consider following table
emp_id country salary
---------------------
1 usa 1000
2 uk 2500
3 uk 1200
4 usa 3500
5 usa 4000
6 uk 1100
Now, my before query is :
Before Query:
select count(emp_id) as count,country from table where salary>2000 group by country;
Before Result:
count country
2 usa
1 uk
After Query:
select count(emp_id) as count,country from table where salary<2000 group by country;
After Query Result:
count country
2 uk
1 usa
My Final Result or Table I want is:
column 1 | column 2 | column 3 | column 4 |
2 usa 2 uk
1 uk 1 usa
...... but if query results are same than it shouldn't show in this table.
Thanks in advance.
I believe that you can use the same approach as here.
select t1.*, t2.* -- if you need specific columns without rn than you have to list them here
from
(
select t.*, row_number() over (order by count) rn
from
(
-- query #1
select count(emp_id) as count,country from table where salary>2000 group by country;
) t
) t1
full join
(
select t.*, row_number() over (order by count) rn
from
(
-- query #2
select count(emp_id) as count,country from table where salary<2000 group by country;
) t
) t2 on t1.rn = t2.rn

Oracle Sql : distinct value in a specific field [duplicate]

This question already has answers here:
How to select records with maximum values in two columns?
(2 answers)
Closed 7 years ago.
I have the following table :
**Country Name Number**
us John 45
us Jeff 35
fr Jean 31
it Luigi 25
fr Maxime 23
ca Justin 23
This table is order by Number. I want to have a query that for each country give me the name with highest number :
**Country Name Number**
us John 45
fr Jean 31
it Luigi 25
ca Justin 23
I try to use distinct but I can't only make it on country if I want to print the all thing...
Have an idea ?'
EDIT :
The table is obtain by a subquery
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE Countries AS
SELECT 'us' AS Country, 'John' AS Name, 45 AS "Number" FROM DUAL
UNION ALL SELECT 'us' AS Country, 'Jeff' AS Name, 35 AS "Number" FROM DUAL
UNION ALL SELECT 'fr' AS Country, 'Jean' AS Name, 31 AS "Number" FROM DUAL
UNION ALL SELECT 'it' AS Country, 'Luigi' AS Name, 25 AS "Number" FROM DUAL
UNION ALL SELECT 'fr' AS Country, 'Maxime' AS Name, 23 AS "Number" FROM DUAL
UNION ALL SELECT 'ca' AS Country, 'Justin' AS Name, 23 AS "Number" FROM DUAL;
Query 1:
SELECT Country,
MAX( Name ) KEEP ( DENSE_RANK FIRST ORDER BY "Number" DESC ) AS "Name",
MAX( "Number" ) AS "Number"
FROM Countries
GROUP BY Country
Results:
| COUNTRY | Name | Number |
|---------|--------|--------|
| ca | Justin | 23 |
| fr | Jean | 31 |
| it | Luigi | 25 |
| us | John | 45 |
I do not have an Oracle db handy but I got this working in my SQL Server db and am pretty sure it will work in Oracle (meaning I think I am using ANSI sql which should work in most db's):
SELECT m.Country,m.Name,m.number
FROM mytable m
INNER JOIN (
select country, MAX(number) as number
FROM mytable GROUP BY Country
) AS tmp ON m.Country = tmp.Country and m.Number = tmp.number
ORDER BY m.Number DESC
This has the added benefit that it should give you records when you have two people in a given country that have the same number.
You didn't give us a table name so I just called it mytable.
Try below query:
SELECT Country, MAX(numbeer) FROM Table_Name GROUP BY Country
PFB updated query to include Name:
SELECT t1.* FROM table1 t1 INNER JOIN
(SELECT country, max(numbeer) as numbeer FROM table1 GROUP BY country) t2
ON t1.country=t2.country AND t1.numbeer=t2.numbeer;
Use row_number():
select t.Country, t.Name, t.Number
from (select t.*,
row_number() over (partition by country order by number desc) as seqnum
from table t
) t
where seqnum = 1;

Specific Ordering in SQL

I have a SQL Server 2008 database. In this database, I have a result set that looks like the following:
ID Name Department LastOrderDate
-- ---- ---------- -------------
1 Golf Balls Sports 01/01/2015
2 Compact Disc Electronics 02/01/2015
3 Tires Automotive 01/15/2015
4 T-Shirt Clothing 01/10/2015
5 DVD Electronics 01/07/2015
6 Tennis Balls Sports 01/09/2015
7 Sweatshirt Clothing 01/04/2015
...
For some reason, my users want to get the results ordered by department, then last order date. However, not by department name. Instead, the departments will be in a specific order. For example, they want to see the results ordered by Electronics, Automotive, Sports, then Clothing. To throw another kink in works, I cannot update the table schema.
Is there a way to do this with a SQL Query? If so, how? Currently, I'm stuck at
SELECT *
FROM
vOrders o
ORDER BY
o.LastOrderDate
Thank you!
You can use case expression ;
order by case when department = 'Electronics' then 1
when department = 'Automotive' then 2
when department = 'Sports' then 3
when department = 'Clothing' then 4
else 5 end
create a table for the departments that has the name (or better id) of the department and the display order. then join to that table and order by the display order column.
alternatively you can do a order by case:
ORDER BY CASE WHEN Department = 'Electronics' THEN 1
WHEN Department = 'Automotive' THEN 2
...
END
(that is not recommended for larger tables)
Here solution with CTE
with c (iOrder, dept)
as (
Select 1, 'Electronics'
Union
Select 2, 'Automotive'
Union
Select 3, 'Sports'
Union
Select 4, 'Clothing'
)
Select * from c
SELECT o.*
FROM
vOrders o join c
on c.dept = o.Department
ORDER BY
c.iOrder