SELECT MIN and MAX across fields and aggregate by user - sql

I have the following raw data saved in the db
id min_price, max_price, min_x, max_x, user_id
-------------------------------------------
1 50 200 5 null 1
2 0 100 0 3 1
3 150 300 0 null 1
4 20 200 2 5 2
5 50 200 0 5 2
6 150 200 1 3 2
I want to create a sql query (postgres) with the following data:
min_price, max_price, min_x, max_x, user_id
0 300 0 null 1
20 200 0 5 2
so basically i would get the min and max for each user_id for difference fields, where null should take precedence over the actual max value,
any idea on how to achieve this via sql?

You can check if NULL exists within that column using COUT(*) vs. COUNT(column):
SELECT
user_id,
CASE WHEN COUNT(*) <> COUNT(max_x) THEN NULL ELSE MAX(max_x) END AS max_x
FROM vt
GROUP BY 1
A brute force solution would be:
NULLIF(MAX(COALESCE(max_x, 9999999999)), 9999999999)

Related

Can't use case & aggregation correctly

I have the following table
Cash_table
ID Cash Rates Amount
1 50 3 16
2 100 4 25
3 130 10 7
3 130 10 6
4 13 7 1.8
5 30 8 2.5
5 30 10 1
6 10 5 2
What I want as a result is to cumulate all the entries that have a Count(id)>1 like this:
ID New_Cash New_Rates New_Amount
1 50 3 16
2 100 4 25
3 130 10+10 130/(10+10)
4 13 7 1.8
5 30 8+10 30/(8+10)
6 10 5 2
So I only want to change the rows where Count(id)>1 and leave the rest like it was.
For the rows with count(id)>1 I want to sum up the rates and take the cash and divide it by the sum of the rates. The Rates alone aren't a problem since I can sum them up and group by id and get the desired result.
The problem is with the New_Amount column:
I am trying to do it with a case statement but it isn't working:
select id,
cash as new_cash,
sum(rates) as new_rates,
(case count(id)
when 1 then amount
else cash/sum(nvl(rates,null))
end) as new_amount
from Cash_table
group by id
As the cash value is always the same for an ID, you can group by that as well:
select id,
cash as new_cash,
sum(rates) as new_rates,
case count(id)
when 1 then max(amount)
else cash/sum(rates)
end as new_amount
from cash_table
group by id, cash
order by id
ID NEW_CASH NEW_RATES NEW_AMOUNT
---------- ---------- ---------- ----------
1 50 3 16
2 100 4 25
3 130 20 6.5
4 13 7 1.8
5 30 18 1.66666667
6 10 5 2
The first branch of the case expression needs an aggregate because you aren't grouping by amount; and the sum(nvl(rates,null)) can just be sum(rates). If you're expecting any null rates then you need to decide how you want the amount to be handled, but nvl(rates,null) isn't doing anything.
You can do the same thing without a case expression if you prefer, manipulating all the values - which might be more expensive:
select id,
cash as new_cash,
sum(rates) as new_rates,
sum(amount * rates)/sum(rates) as new_amount
from cash_table
group by id, cash
order by id

eliminating all the null values and showing all the values in one record

I need to make a select statement to table1 so that it looks like table2 oracle. Please help. I tried some case when and pivot but I failed again and again.
table1:
Product abc def ghi
1 100 0 0
1 0 10 0
1 0 0 20
2 0 0 80
2 0 60 0
2 3 0 0
table2:
product abc def ghi
1 100 10 20
2 3 60 80
Do aggregation :
select product, max(abc), max(def), max(ghi)
from table1 t1
group by product;
However, the value you have provided with sample data the sum() would also work. If, table has NULL instead of 0 then both function would work but i would prefer to use max() if the null values in table.

SQL Query. limit an update per rows if condition is X and Y for the same ID number

Have the following table tblTrans where
Trans_ID Trans Sequence Trans_PointsEarned Trans_PointsApplied
4452 1 1 1
4452 2 1 1
4452 3 0 1
4462 1 1 1
4462 2 1 1
4462 3 1 1
4462 4 1 1
4462 5 1 1
9101 1 0 1
9101 2 0 1
9101 3 0 1
9101 4 0 1
(useless table doesnt work)
I need to set the following on another field per every customer ID.
So Customer_OverallPoints
4452 = 2 (doesn't count 0's)
4462 = 4 (I want to cap the points to 4 based on the sequence and transID and customerID)
9101 = 0 (dont count 0's).
This needs to be applied to thousands of records based on customerID and TransID where Trans_Sequence is within the same Trans_ID and it only counts the first 4 rows that have the Trans_pointsEarned = 1.
I tried putting a psuedocode together but it just looked ridicilous and I can't even come up with the logic for this.
Thanks
Assuming that TransId is really the customer id, I think the basic logic is just an aggregation:
select t.TransId,
(case when sum(t.Trans_PointsEarned) > 4 then 4
else sum(t.Trans_PointsEarned)
end) as Customer_OverallPoints
from tblTrans t
group by t.TransId;
You can put this into an update statement as:
update customers c
set Customer_OverallPoints = (select (case when sum(t.Trans_PointsEarned) > 4 then 4
else sum(t.Trans_PointsEarned)
end)
from tblTrans t
where t.TransId = c.CustomerId
);

Inserting a new indicator column to tell if a given row maximizes another column in SQL

I currently have a table in SQL that looks like this
PRODUCT_ID_1 PRODUCT_ID_2 SCORE
1 2 10
1 3 100
1 10 3000
2 10 10
3 35 100
3 2 1001
That is, PRODUCT_ID_1,PRODUCT_ID_2 is a primary key for this table.
What I would like to do is use this table to add in a row to tell whether or not the current row is the one that maximizes SCORE for a value of PRODUCT_ID_1.
In other words, what I would like to get is the following table:
PRODUCT_ID_1 PRODUCT_ID_2 SCORE IS_MAX_SCORE_FOR_ID_1
1 2 10 0
1 3 100 0
1 10 3000 1
2 10 10 1
3 35 100 0
3 2 1001 1
I am wondering how I can compute the IS_MAX_SCORE_FOR_ID_1 column and insert it into the table without having to create a new table.
You can try like this...
Select PRODUCT_ID_1, PRODUCT_ID_2 ,SCORE,
(Case when b.Score=
(Select Max(a.Score) from TableName a where a.PRODUCT_ID_1=b. PRODUCT_ID_1)
then 1 else 0 End) as IS_MAX_SCORE_FOR_ID_1
from TableName b
You can use a window function for this:
select product_id_1,
product_id_2,
score,
case
when score = max(score) over (partition by product_id_1) then 1
else 0
end as is_max_score_for_id_1
from the_table
order by product_id_1;
(The above is ANSI SQL and should run on any modern DBMS)

TSQL - divide rows into groups based on one field

This is modified version of my earlier question: TSQL equally divide resultset to groups and update them
I have my database with 2 tables like so:
Orders table has data like below:
OrderID OperatorID GroupID OrderDesc Status Cash ...
--------------------------------------------------------------------------
1 1 1 small_order 1 300
2 1 1 another_order 1 0
3 1 2 xxxxxxxxxxx 2 1000
5 2 2 yyyyyyyyyyy 2 150
9 5 1 xxxxxxxxxxx 1 50
10 NULL 2 xxxxxxxxxxx 1 150
11 NULL 3 xxxxxxxxxxx 1 -50
12 4 1 xxxxxxxxxxx 1 200
Operators table:
OperatorID Name GroupID Active
---------------------------------------
1 John 1 1
2 Kate 1 1
4 Jack 2 1
5 Will 1 0
6 Sam 3 0
I'm able to equally divide my recordset into equally groups using below query:
SELECT o.*, op.operatorName AS NewOperator, op.operatorID AS NewOperatorId
FROM (SELECT o.*, (ROW_NUMBER() over (ORDER BY newid()) % numoperators) + 1 AS randseqnum
FROM Orders o CROSS JOIN
(SELECT COUNT(*) AS numoperators FROM operators WHERE operators.active=1) op
WHERE o.status in (1,3)
) o JOIN
(SELECT op.*, ROW_NUMBER() over (ORDER BY newid()) AS seqnum
FROM Operators op WHERE op.active=1
) op
ON o.randseqnum = op.seqnum ORDER BY o.orderID
Demo available at: http://sqlfiddle.com/#!3/ff47b/1
Using script from above I can divide Orders to (almost) equal groups but based on number or Orders for Operator, but I need to modify it so that it will assign Operators to Orders based on sum or Cash for orders.
For example:
If I have 6 Orders with Cash values: 300, 0, 50, 150, -50, 200 they sum gives 650.
My script should assign to 3 Operators random 2 Orders with random sum of Cash for Orders.
What I would like to get is to assign for example 300,-50 to operator1, 200, 0 to second and 150, 50 to third.
Hope this sound clear :)
Here is example output that I expect to get:
ORDERID OPERATORID GROUPID DESCRIPTION STATUS CASH NEWOPERATORID
------------------------------------------------------------------------
1 1 1 small_order 1 300 2
2 1 1 another_order 1 0 1
9 5 1 xxxxxxxxxxx 1 50 4
10 (null) 2 xxxxxxxxxxx 1 150 4
11 (null) 3 xxxxxxxxxxx 1 -50 2
12 4 1 xxxxxxxxxxx 1 200 1
How can I (if I can at all) assign Operators to my Orders so that sum or Cash will be closest to average
If I'm understanding this right, could you get the result you want by ordering the Cash column by the biggest, then the smallest, then the next biggest, then the next smallest, etc. Like this:
ROW_NUMBER() over (order by CASE WHEN CashOrder % 2 = 1 then Cash else -Cash END) as splitCash
where you've provided CashOrder lower in the query with
ROW_NUMBER() over (ORDER by CASH) as CashOrder
Then you specify each of your operators depending on this split value, ie (for three operators):
splitCash%3 +1