How can you choose the line with the higher value in SQL? - sql

Let's say we have this table:
----------
number |name| price
----------
1 | a | 2
----------
1 | b | 4
----------
2 | c | 4
----------
3 | d | 6
----------
I want to pick the numbers and their highest price. So how can i select only the line with number 1, which has the higher price and not show the other one?. My problem is the second column.

Easy: GROUP BY
select number, max(price)
from your_table
group by number;
If you have more columns and you want to select the row with max price, use this:
select *
from (select
t.*,
row_number() over (partition by number order by price desc) rn
from your_table t) t
where rn = 1;

In Postgres, the most efficient way to get the row with the highest price for each number is to use distinct on:
select distinct on (number) t.*
from t
order by number, price desc;

Related

How to Order by in Nested Query?

I have a table like this :
MY_TABLE
Id Name Brand Date
-- ---- ----- ------
1 ABC 1 18-09-2019
2 XYZ 1 19-09-2019
3 MNO 1 18-09-2019
4 ABC 1 19-09-2019
5 PQR 2 17-06-2020
6 MNO 1 19-03-2019
7 ABC 2 19-09-2019
I want to write a query in such a way that first i need to sort based on Date. Based on that result i have to sort count(Name) for a brand.Like this
ID Name Count(Name) Brand date
--- ---- ---------- ------ -----
1 ABC 2 1 19-09-2019
2 XYZ 1 1 19-09-2019 // Eventhough count is less it came second because 19-09-2019 is latest than 19-03-2019
3 MNO 2 1 18-09-2019
You can group by name and aggregate:
select
name,
count(*) counter,
max(brand) brand,
max(trunc(datee)) maxdate
from my_table
where brand = 1
group by name
order by maxdate desc, counter desc
I used trunc(datee) because your dates (as we found out) contain a time part.
See the demo.
Results:
> NAME | COUNTER | BRAND | MAXDATE
> :--- | ------: | ----: | :--------
> ABC | 2 | 1 | 19-SEP-19
> XYZ | 1 | 1 | 19-SEP-19
> MNO | 2 | 1 | 18-SEP-19
You have to first create the count using a group by and then sort by using order by which supports multiple fields (comma separated). And once you give an alias to the column with the count you an use this alias in the order by:
select name, brand, min(date), count(name) as NumRecords, min(id) as id
from my_table
group by name, brand
order by date, NumRecords
I added the min(id) because it seems that you are trying to show the minimum id.
I think you want to select one row per name/brand pair based on the highest count on the most recent date:
select t.*
from (select t.*,
row_number() over (partition by name, brand order by date desc, count desc) as seqnum
from t
) t
where seqnum = 1;
SELECT name, COUNT(*) BrandCount, MAX(brand) brand, date from my_table
ORDER BY date
You may use [ ASC | DESC ] in the end depending upon requirement.
Similar query.
Hope it sorts
Is this what you want?
SELECT MIN(ID), Name,
Count(Name), Brand, MIN(date) as
Date_MIN
From Table where Brand =1 Group by
Brand,Name
Order by Date_MIN desc,Count(Name)
Desc;
;

Need to sum all Most Recent Rows from each Store that have ItemID

I have a table with, among other things, these columns: DateTransferred, ComputedQuantity, StoreID, ItemID
I have two goals. My simpler goal is to write a query where I feel in the ItemID and it sums up the ComputedQuantity where it matches that ItemID, only using the most recent DateTransferred for each StoreID. So with the following example data:
DateTransferred | StoreID | ItemID | ComputedQuantity
11/10/17 | 1 | 1 | 3 <
10/10/17 | 1 | 1 | 4
09/10/17 | 2 | 1 | 9 <
08/10/17 | 3 | 1 | 1 <
07/10/17 | 3 | 1 | 10
I would want it to pull every row with < next to it, as that's the most recent Date for that StoreID, and sum up to 13
My more complicated goal is that I would like to include the above-calculated value into a 'join' where I'm dealing with the Item table, so that I can pull all the items and join them with a new column which has the summed up ComputedQuantity
This is on SQL Server 10 on Windows Server 2008, if that matters
One simple method uses a correlated subquery:
select t.*
from t
where t. DateTransferred = (select max(t2.DateTransferred)
from t t2
where t2.storeid = t.storeid
);
Another even simpler method uses window functions:
select t.*
from (select t.*,
row_number() over (partition by storeid order by DateTransferred desc) as seqnum
from t
) t
where seqnum = 1;
In either case, you can add a where clause to the subquery if you want the most recent date on or before some given date (say a year ago).
Also, these both assume that your data has no future dates. If so, then add where DateTransferred < getdate().
The final statement which sums the ComputedQuantities:
select ItemID, SUM(ComputedQuantity) Quantity
from (select t.*,
row_number() over (partition by StoreID, ItemID order by DateTransferred DESC) as seqnum
from [db].[dbo].[InventoryTransferLog] t
) t
where seqnum = 1 and ComputedQuantity > 0
GROUP BY ItemID
ORDER BY ItemID
I decided not to sum values < 0

INSERT SELECT loop

I am trying to transfer data from one table to another. But in the process I need to do something extra I am just wondering is it possible to do something like this in SQL or PL/SQL alone.
source target
------------------- ------------------------
| id | name | qty | | id | source_id | qty |
------------------- ------------------------
| 1 | test | 2 | | 1 | 1 | 1 |
------------------- ------------------------
| 2 | ago | 1 | | 2 | 1 | 1 |
------------------- ------------------------
| 3 | 2 | 1 |
-----------------------
Here based on the quantity in source table I will have to insert multiple records. Quantity could be of any number. ID in target table is auto incremented. I tried this
INSERT INTO target (SELECT id, qty FROM source);
But this does not take care of the qty loop.
Plain SQL:
with
inputs ( id, qty ) as (
select 1, 2 from dual union all
select 2, 1 from dual union all
select 3, 5 from dual
)
-- end of test data; solution (SQL query) begins below this line
select row_number() over (order by id) as id, id as source_id, 1 as qty
from inputs
connect by level <= qty
and prior id = id
and prior sys_guid() is not null
;
NOTE - if the id is generated automatically, just drop the row_number().... as id column; the rest is unchanged.
ID SOURCE_ID QTY
-- --------- --
1 1 1
2 1 1
3 2 1
4 3 1
5 3 1
6 3 1
7 3 1
8 3 1
This is possible using SQL. Use CTE to generate amount of rows that matches your maximum qty from source table and use non-equi JOIN for generating rows. Use row_number analytic function to assign each row it's unique id (if you have it in your target table, check below on my Edit):
with gen_numbers(r) as (
select rownum r
from dual
connect by rownum <= (select max(qty) from src) -- our maximum limit of rows needed
)
select
row_number() over (order by src.id) as id,
src.id as source_id,
1 as qty
from src
join gen_numbers on src.qty <= gen_numbers.r; -- clone rows qty times
Note that you can safely put in constans value 1 in the output of qty.
Your test data:
create table src (id int, name varchar(255), qty int);
insert into src (id, name, qty)
select 1, 'test', 2 from dual union all
select 2, 'ago', 1 from dual
;
Result:
ID SOURCE_ID QTY
1 2 1
2 2 1
3 1 1
Edit: Since your target id column is auto incremented, you don't need the row_number. Just specify it like that to perform an INSERT:
with gen_numbers(r) as (
select rownum r
from dual
connect by rownum <= (select max(qty) from src) -- our maximum limit of rows needed
)
insert into target_table(source_id, qty)
select
src.id as source_id,
1 as qty
from src
join gen_numbers on src.qty <= gen_numbers.r; -- clone rows qty times
order by src.id
Notice that I've added an ORDER BY clause to ensure proper ordering of inserting values.
INSERT INTO TARGET(source_id, qty)
WITH
output
AS
(
SELECT id, qty FROM source
UNION ALL
SELECT id, qty - 1 FROM source WHERE qty > 1
)
SELECT
id, count(*) as qty
FROM output
group by
id, quantity
ORDER BY
id

Postgres: select all row with count of a field greater than 1

i have table storing product price information, the table looks similar to, (no is the primary key)
no name price date
1 paper 1.99 3-23
2 paper 2.99 5-25
3 paper 1.99 5-29
4 orange 4.56 4-23
5 apple 3.43 3-11
right now I want to select all the rows where the "name" field appeared more than once in the table. Basically, i want my query to return the first three rows.
I tried:
SELECT * FROM product_price_info GROUP BY name HAVING COUNT(*) > 1
but i get an error saying:
column "product_price_info.no" must appear in the GROUP BY clause or be used in an aggregate function
SELECT *
FROM product_price_info
WHERE name IN (SELECT name
FROM product_price_info
GROUP BY name HAVING COUNT(*) > 1)
Try this:
SELECT no, name, price, "date"
FROM (
SELECT no, name, price, "date",
COUNT(*) OVER (PARTITION BY name) AS cnt
FROM product_price_info ) AS t
WHERE t.cnt > 1
You can use the window version of COUNT to get the population of each name partition. Then, in an outer query, filter out name partitions having a population that is less than 2.
Window Functions are really nice for this.
SELECT p.*, count(*) OVER (PARTITION BY name) FROM product p;
For a full example:
CREATE TABLE product (no SERIAL, name text, price NUMERIC(8,2), date DATE);
INSERT INTO product(name, price, date) values
('paper', 1.99, '2017-03-23'),
('paper', 2.99, '2017-05-25'),
('paper', 1.99, '2017-05-29'),
('orange', 4.56, '2017-04-23'),
('apple', 3.43, '2017-03-11')
;
WITH report AS (
SELECT p.*, count(*) OVER (PARTITION BY name) as count FROM product p
)
SELECT * FROM report WHERE count > 1;
Gives:
no | name | price | date | count
----+--------+-------+------------+-------
1 | paper | 1.99 | 2017-03-23 | 3
2 | paper | 2.99 | 2017-05-25 | 3
3 | paper | 1.99 | 2017-05-29 | 3
(3 rows)
Self join version, use a sub-query that returns the name's that appears more than once.
select t1.*
from tablename t1
join (select name from tablename group by name having count(*) > 1) t2
on t1.name = t2.name
Basically the same as IN/EXISTS versions, but probably a bit faster.
SELECT name, count(name)
FROM product_price_info
GROUP BY name
HAVING COUNT(name) > 1
LIMIT 3

Rank function for date in Oracle SQL

I have the following code for example:
SELECT id, order_day, purchase_id FROM d
customer_id and purchase_id are unique. Each customer_id could have multiple purchase_id. Assume every one has made at least 5 orders.
Now, I just want to pull the first 5 purchase IDs of each customers ID (this depends on the earliest dates of purchases). I want the result to look like this:
id | purchase_id | rank
-------------------------
A | WERFEW43 | 1
A | ERTGDSFV | 3
A | FDGRT45 | 2
A | BRTE4TEW | 4
A | DFGDV | 5
B | DSFSF | 1
B | CF345 | 2
B | SDFSDFSDFS | 4
I thought of Ranking order_day, but my knowledge is not good enough to pull this off.
select id,purchase_id, rank() over (order by order_day)
from d
you also can try dense_rank() over (order by order_day) and row_number() over (order by order_day) and choose which one will be more suitable for you
select *
from
( SELECT
id
,order_day
,purchase_id
,row_number() -- ranking
over (partition by id -- each customer
order by order_day) as rn -- based on oldest dates
FROM d
) as dt
where rn <= 5