SQL bring back highest sum of rows - filter

I'm looking to calculate the highest basket in my set of data but I can't get my head around how I should do it.
I have data like:
OrderID | CustomerID | BasketID | ProductID | Price
1 | 1 | 1 | 221 | 10
2 | 1 | 1 | 431 | 123
3 | 1 | 2 | 761 | 44
4 | 2 | 3 | 12 | 54
5 | 2 | 3 | 102 | 78
6 | 3 | 4 | 111 | 98
7 | 3 | 4 | 41 | 45
8 | 3 | 5 | 65 | 66
9 | 4 | 6 | 32 | 47
10 | 4 | 6 | 118 | 544
Sorry if it seems quite messy.
But I can easily get the SUM with an obvious
SELECT SUM([Price]), BasketID, CustomerID FROM table
GROUP BY BasketID, CustomerID
But how can I filter the list for only the highest priced Basket ID for that CustomerID
Thanks

You can use a CTE (Common Table Expression) with the ROW_NUMBER function:
;WITH HighestPricePerCustomerAndBasket AS
(
SELECT
ID, UserID, ClassID, SchoolID, Created,
ROW_NUMBER() OVER(PARTITION BY BasketID,CustomerID ORDER BY Price DESC) AS 'RowNum'
FROM dbo.YourTable
)
SELECT
[Price], BasketID, CustomerID
FROM HighestPricePerCustomerAndBasket
WHERE RowNum = 1
This CTE "partitions" your data by BasketID,CustomerID, and for each partition, the ROW_NUMBER function hands out sequential numbers, starting at 1 and ordered by Price DESC - so the first row (highest price) gets RowNum = 1 (for each BasketID,CustomerID "partition") which is what I select from the CTE in the SELECT statement after it.

SELECT *
FROM (SELECT *,
DENSE_RANK() OVER (PARTITION BY CustomerID ORDER BY BasketTotal DESC) AS RNK
FROM (SELECT Sum(Price) AS BasketTotal,
BasketID,
CustomerID
FROM Order a
GROUP BY BasketID,
CustomerID
) a
) b
WHERE RNK = 1
I managed to conjure something up that worked.

Related

Oracle select from values and substract from last value

Need help on Oracle SELECT statement.
I Have table like this with used days. User can have 11 days
+----+----------+-----+-----------+
| ID | NAME | USED| DATE |
+----+----------+-----+-----------+
| 1 | John | 1 |01/01/2018 |
| 2 | John | 2 |01/03/2018 |
| 3 | John | 2 |01/05/2018 |
+----+----------+-----+-----------+
So on QUERY SELECT i want to have result of left days like this
+----+----------+------+-----------+
| ID | NAME | DAYS | USED| LEFT|
+----+----------+------+-----------+
| 1 | John | 11 | 1 | 10 |
| 2 | John | 10 | 2 | 8 |
| 3 | John | 8 | 2 | 6 |
+----+----------+------+-----------+
Any help how to achieve this result ?
The main thing you need is the analytic version of sum. Also, try not to use Oracle keywords (like date) as column names.
-- sample data
with ex as (select 1 as id, 'John' as name, 1 as used, to_date('01/01/2018','mm/dd/yyyy') as date1 from dual
union select 2 as id, 'John' as name, 2 as used, to_date('01/03/2018','mm/dd/yyyy') from dual
union select 3 as id, 'John' as name, 2 as used, to_date('01/05/2018','mm/dd/yyyy') from dual)
-- main query
select id,
name,
11-sum(used) over (partition by name order by date1) + used as days,
used,
11-sum(used) over (partition by name order by date1) as left
from ex;
Output:
ID NAME DAYS USED LEFT
---------- ---- ---------- ---------- ----------
1 John 11 1 10
2 John 10 2 8
3 John 8 2 6

Row count by group

I am attempting to write the following query to get a row count by group.
select
a.employee, a.cov_option,
count(a.cov_option) over (partition by a.cov_option order by a.employee) as row_num
from wilson.benefit a
inner join wilson.bncategory b
ON a.plan_type = b.plan_type and a.plan_option = b.plan_option
inner join wilson.bncovopt c
ON a.company = c.company and a.plan_code = c.plan_code and a.cov_option = c.coverage_opt
where
a.plan_type = 'HL' and
to_char(a.stop_date, 'yyyy-mm-dd') = '1700-01-01'
order by a.employee, a.cov_option
The result set returned is:
employee | cov_option |row_num
-------------|--------------|--------------
429 | 1 | 1
429 | 3 | 2
429 | 3 | 2
1420 | 1 | 2
1420 | 3 | 4
1420 | 3 | 4
1537 | 2 | 2
1537 | 2 | 2
The result set I am attempting to return is:
429 | 1 | 1
429 | 3 | 2
429 | 3 | 2
1420 | 1 | 1
1420 | 3 | 2
1420 | 3 | 2
1537 | 2 | 1
1537 | 2 | 1
What you seem to want is dense_rank() rather than count(). Indeed, "count" means simply determining how many rows are in each group, it is not "counting" in the way we learn as children (first, second, third). That kind of counting is called "ranking".
dense_rank() over (partition by a.employee order by a.cov_option) as row_num
should do what you need.
There is also rank() - the difference is that if two rows are tied for first, with dense_rank() the third row gets rank 2; with simple rank() it gets rank 3 (rank 2 is "used up" by the first two rows).

Return a value if null

In oracle, I have a piece of code that returns our work order pass rate. It essentially takes the number of failures (MRB) and divides by the number of work orders processed (WO) to give the rate. The problem I have is that unless there is a failure (MRB) I will not get a return for the corresponding month. If there is no MRB failure, I would like a return of 100% for that month. Here is the code I am working with. For the record, I am not a programmer, I've just picked up a bit of info along the way. I have tried adding NVL but that does not seem to help.
Select To_Char(MTH, 'Month') As "Month",WO, MRB,
Round(1 - (MRB / WO), 3) * 100 As "Pass Rate",
'98' As Goal
From
(
Select Trunc(g.START_DATE, 'Month') As mth,
Count(Distinct V_PDAYPROD_CRW1.PDAYPROD_ID) As WO,
Count(Distinct V_WF_HEADER_MRB.ID) As MRB
From GLPERIODS g
Left Join V_PDAYPROD_CRW1 On Trunc(g.START_DATE, 'Month') = Trunc(V_PDAYPROD_CRW1.PROD_DATE, 'Month')
Left Join V_WF_HEADER_MRB On Trunc(g.START_DATE, 'Month') = Trunc(V_WF_HEADER_MRB.OPEN_DATE, 'Month')
Inner Join ARINVT On V_PDAYPROD_CRW1.ARINVT_ID = ARINVT.ID
Where Extract(Year From g.START_DATE) = Extract(Year From SysDate)
And V_WF_HEADER_MRB.WF_TYPE_ID = '99'
And V_WF_HEADER_MRB.EPLANT_ID = 2
And ARINVT.EPLANT_ID = 2
Group By Trunc(g.START_DATE, 'Month'),
V_WF_HEADER_MRB.WF_TYPE_ID
)
Group By To_Char(MTH, 'Month'),WO,MRB,
Round(1 - (MRB / WO), 3) * 100,
'98',MTH
Order By MTH
The above code returns the following:
MONTH | WO | MRB | Pass Rate | GOAL
September | 60 | 1 | 98.3 | 98
December | 30 | 2 | 93.3 | 98
I would like it to return something like this:
MONTH | WO | MRB | Pass Rate | GOAL
January | 25 | 0 | 100 | 98
February | 66 | 0 | 100 | 98
March | 35 | 0 | 100 | 98
April | 22 | 0 | 100 | 98
May | 19 | 0 | 100 | 98
June | 47 | 0 | 100 | 98
July | 52 | 0 | 100 | 98
August | 55 | 0 | 100 | 98
September | 60 | 1 | 98.3 | 98
October | 39 | 0 | 100 | 98
November | 18 | 0 | 100 | 98
December | 30 | 2 | 93.3 | 98
Thank you for any help you can offer.
If you expect all the records from the main table to be returned in the result set then you should not use the filtering criteria on "v_wf_header_mrb" table in the where clause.
Remove "V_WF_HEADER_MRB.WF_TYPE_ID = '99' And V_WF_HEADER_MRB.EPLANT_ID = 2 " condition from Where clause and try.
You can use the below query instead.
SELECT
TO_CHAR(mth,'Month') AS "Month",
wo,
mrb,
round(1 - (mrb / wo),3) * 100 AS "Pass Rate",
'98' AS goal
FROM
(
SELECT
trunc(g.start_date,'Month') AS mth,
COUNT(DISTINCT v_pdayprod_crw1.pdayprod_id) AS wo,
COUNT(DISTINCT v_wf_header_mrb.id) AS mrb
FROM
glperiods g
LEFT JOIN
v_pdayprod_crw1
ON
trunc(g.start_date,'Month') = trunc(v_pdayprod_crw1.prod_date,'Month')
LEFT JOIN
v_wf_header_mrb
ON
trunc(g.start_date,'Month') = trunc(v_wf_header_mrb.open_date,'Month')
AND
v_wf_header_mrb.wf_type_id = '99'
AND
v_wf_header_mrb.eplant_id = 2
INNER JOIN
arinvt
ON
v_pdayprod_crw1.arinvt_id = arinvt.id
WHERE
EXTRACT(YEAR FROM g.start_date) = EXTRACT(YEAR FROM SYSDATE)
AND
arinvt.eplant_id = 2
GROUP BY
trunc(g.start_date,'Month'),
v_wf_header_mrb.wf_type_id
)
GROUP BY
TO_CHAR(mth,'Month'),
wo,
mrb,
round(1 - (mrb / wo),3) * 100,
'98',
mth
ORDER BY mth;

max() issue in Oracle SQL [duplicate]

This question already has answers here:
Fetch the rows which have the Max value for a column for each distinct value of another column
(35 answers)
Closed 8 years ago.
I'm using Oracle SQL and i need some help with max() function.
I have the following table:
ID | Type | Price | Quantity
1 | A | 10 | 2
2 | B | 5 | 5
3 | C | 10 | 3
4 | A | 8 | 7
5 | A | 6 | 9
6 | A | 7 | 5
7 | B | 15 | 3
8 | A | 20 | 4
9 | A | 3 | 7
10 | B | 11 | 8
I need to aggregate the table by Type column. For each group of Type (A, B, C), i need to select the price and the quantity of max(id).
I this case:
ID | Type | Price | Quantity
9 | A | 3 | 7
10 | B | 11 | 8
3 | C | 10 | 3
Any Suggestion?
max won't help you with this. You can use the row_number partitioning function.
select id, type, price, quantity
from
(
select yourtable.*,
row_number() over (partition by type order by id desc) rn
from yourtable
) v
where rn = 1
Something like this:
Select t.* From
(Select Max(ID) As ID From Table
Group By Type) tmp
Join Table t On t.ID = tmp.ID

Oracle select two (or more) adjacent rows having the same value for a given column

How do I do the following in Oracle:
I have a (simplified) table:
+-----+-----+-----+
| a | b | ... |
+-----+-----+-----+
| 1 | 7 | ... |
| 2 | 5 | ... |
| 1 | 7 | ... |
+-----+-----+-----+
Where a functions as a unique identifier for a person, and b is the field I am interested in matching across rows. How do I construct a query that basically says "give me the person-ID's where the person has multiple b values (i.e., duplicates)"?
So far I have tried:
SELECT a FROM mytable GROUP BY a HAVING COUNT(DISTINCT b) > 1;
This feels close except it just gives me the user IDs where the user has multiple unique b's, which I suspect is coming from the DISTINCT part, but I'm not sure how to change the query to achieve what I want.
Try
group by a,b having count(b) > 1
Yours would count 7,5,7 as 2 (one 7, one 5). This one one will count total Bs in any grouping, so you'll get 1,7 - > 2 and 1,5 -> 1
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE mytable ( a, b ) AS
SELECT LEVEL, LEVEL FROM DUAL CONNECT BY LEVEL <= 2000
UNION ALL
SELECT LEVEL *2, LEVEL * 2 FROM DUAL CONNECT BY LEVEL <= 1000;
Query 1:
WITH data AS (
SELECT a
FROM mytable
GROUP BY a
HAVING COUNT(b) > COUNT( DISTINCT b )
ORDER BY a
),
numbered AS (
SELECT a,
ROWNUM AS rn
FROM data
)
SELECT a
FROM numbered
WHERE rn <= 20
Results:
| A |
|----|
| 2 |
| 4 |
| 6 |
| 8 |
| 10 |
| 12 |
| 14 |
| 16 |
| 18 |
| 20 |
| 22 |
| 24 |
| 26 |
| 28 |
| 30 |
| 32 |
| 34 |
| 36 |
| 38 |
| 40 |

Resources