Apply 2 calcul in the same column - Sql server - sql

I want to apply a calculation in a column in 2 cases:
When the Etablissements in: ("E10", "E20")
Then apply this calcul: Price * 10 in the column name: Calcul
When Etablissement in ("E30", "E40") then apply another calcul in the same column Calcul: Price * 50
I wish that you get it, and thank you.

You just want CASE expression :
SELECT t.*,
(CASE WHEN Etablissements in ('E10','E20')
THEN Price*10
WHEN Etablissement in ('E30','E40')
THEN Price*50
END) AS Calcul
FROM table t;

Related

How do I get the closest match vlookup for entire column in Google Big Query SQL?

I am trying to take a column of original prices and enter a discount % and return the closest match to a predetermined set of values. These allowable values are found in another table that is just one column of prices. I am curious to hear how ties would be handled. Please note that this is for a long list of items, so this would have to apply to an entire column. The specific syntax needed is Google Big Query.
I envision this functioning similarly to excel's VLOOKUP approximate = 1. In practice, I will apply the same solution to multiple price points in the results table (ex. origPrice, 25%off, 50%off, and 75%off etc. ), but I figured that I could copy-paste the solution multiple times.
The below example shows a 50% price reduction.
allowableDiscounts
discountPrice
$51.00
$48.50
$40.00
productInfo
Item
OrigPrice
Apple
$100.00
Banana
$ 98.00
Desired Output
Item
OrigPrice
exact50off
closestMatch
Apple
$100.00
$50.00
$51.00
Banana
$ 98.00
$44.00
$40.00
I have researched solutions here and elsewhere. Most of what I found suggested sorting the allowableDiscounts table by the absolute value of the difference between exact50off and discountPrice. That worked great for one instance, but I could not figure out how to apply that to an entire list of prices.
I have workarounds both in SQL and excel that can accomplish the same task manually, but I am looking for something to match the above function so that way if the allowableDiscounts table changes, the calculations will reflect that without recoding.
SELECT
p.Item,
p.OrigPrice,
p.OrigPrice * 0.5 AS exact50off
--new code from allowableDiscounts.discountPrice
FROM
productInfo AS p
WHERE
--filters applied as needed
You may work it out with a CROSS JOIN, then compute the smallest difference and filter out the other generated records (with higher differences).
Smallest difference here is retrieved by assigning a rank to all differences in each partition <Item, OrigPrice> (with ROW_NUMBER), then all values ranked higher than 1 are discarded.
WITH cte AS (
SELECT *,
OrigPrice*0.5 AS exact50off,
ROW_NUMBER() OVER(PARTITION BY Item, OrigPrice ORDER BY ABS(discountPrice - OrigPrice*0.5)) AS rn
FROM productInfo
CROSS JOIN allowableDiscounts
)
SELECT Item,
OrigPrice,
exact50off,
discountPrice
FROM cte
WHERE rn = 1
In case the tables are large, as you stated, a cross join is not possible and a window function is the only solution.
First we generate a function nearest, which return the element (x or y) closest to a target value.
Then we define both tables, discountPrice and productInfo. Next, we union these tables as helper. The first column tmp holds the value 1, if the data is from the main table productInfo and we calculate the column exact50off. For the table discountPrice the tmp column in set to 0 and the exact50off column is filled with the entries discountPrice. We add the table discountPrice again, but for column exact75off.
We query the helper table and use:
last_value(if(tmp=0,exact50off,null) ignore nulls) over (order by exact50off),
tmp=0 : Keep only entries from the table discountPrice
last_value get nearest lowest value from table discountPrice
We run the same again, but with desc to obtain the nearest highest value.
The function nearest yields the nearest values of both.
Analog this is done for exact75off
create temp function nearest(target any type,x any type, y any type) as (if(abs(target-x)>abs(target-y),y,x) );
with allowableDiscounts as (select * from unnest([51,48.5,40,23,20]) as discountPrice ),
productInfo as (select "Apple" as item, 100 as OrigPrice union all select "Banana",98 union all select "Banana cheap",88),
helper as (
select 1 as tmp, # this column holds the info from which table the rows come forme
item,OrigPrice, # all columns of the table productInfo (2)
OrigPrice/2 as exact50off, # calc 50%
OrigPrice*0.25 as exact75off, # calc 75%
from productInfo
union all # table for 50%
select 0 as tmp,
null,null, # (2) null entries, because the table productInfo has two columns (2)
discountPrice as exact50off, #possible values for 50% off
null # other calc (75%)
from allowableDiscounts
union all # table for 75%
select 0 as tmp,
null,null, # (2) null entries, because the table productInfo has two columns (2)
null, # other calc (50%)
discountPrice, #possible values for 75% off
from allowableDiscounts
)
select *,
nearest(exact50off,
last_value(if(tmp=0,exact50off,null) ignore nulls) over (order by exact50off),
last_value(if(tmp=0,exact50off,null) ignore nulls) over (order by exact50off desc)
) as closestMatch50off,
nearest(exact75off,
last_value(if(tmp=0,exact75off,null) ignore nulls) over (order by exact75off),
last_value(if(tmp=0,exact75off,null) ignore nulls) over (order by exact75off desc)
) as closestMatch75off,
from helper
qualify tmp=1
order by exact50off
Yet another approach
create temp function vlookup(data array<float64>, key float64)
returns string language js as r'''
closestMatch = null;
closestDifference = Number.MAX_VALUE;
for (let i = 0; i < data.length; i++) {
difference = Math.abs(data[i] - key);
if (difference < closestDifference) {
closestMatch = data[i];
closestDifference = difference;
}
}
return closestMatch;
''';
with priceOffList as (
select *
from unnest([25, 50, 75]) off
)
select * from (
select Item, OrigPrice, off, offPrice, vlookup(arr, offPrice) as closestMatch
from productInfo,(select array_agg(discountPrice order by discountPrice) arr from allowableDiscounts), priceOffList
,unnest([OrigPrice * off / 100]) as offPrice
)
pivot (any_value(offPrice) offPrice, any_value(closestMatch) closestMatch for off in (25, 50, 75))
if applied to sample data in your question - output is
Use the ABS(X) function to compute the absolute values between the columns in the tables to make a match as an exact match or a difference in values between 1 and 4 for the various discount values as below, use a LEFT JOIN to get allow values in your leading table productInfo and either matching values or NULL from the allowableDiscounts table.
SELECT
p.Item,
p.OrigPrice,
p.OrigPrice * 0.5 AS exact50off,
p.OrigPrice * 0.25 AS exact25off,
p.OrigPrice * 0.75 AS exact75off,
q.discountPrice AS closestMatch
FROM
productInfo AS p
LEFT JOIN allowableDiscounts q on ABS(p.OrigPrice * 0.50 - q.discountPrice) = 0
OR ABS(p.OrigPrice * 0.50 - q.discountPrice) BETWEEN 0.01 AND 4.0
OR ABS(p.OrigPrice * 0.25 - q.discountPrice) = 0
OR ABS(p.OrigPrice * 0.75 - q.discountPrice) = 0
OR ABS(p.OrigPrice * 0.25 - q.discountPrice) BETWEEN 0.01 AND 4.0
OR ABS(p.OrigPrice * 0.75 - q.discountPrice) BETWEEN 0.01 AND 4.0;

BigQuery Sum Columns with Case When

I want to calculate the sum of the column with an if condition. Here my code:
SELECT
SUM(CASE WHEN properties = 'revenue' THEN property.value ELSE 0 END) AS TotalRevenue,
FROM deneme-319116.events.eventstable;
Sample data:
Properties Property.value
Search / null
Revenue / 15
Count / 25
Revenue / 40
I need to find 55 (15+40) as a result
In my eventstable I have columns properties and the property.value. I want to sum the property.value values that properties equal to 'revenue' but bigquery gives error: 'cannot acces field key on a value with type ARRAYvalue....
Plz help, thx
Hmmm . . . then you need to unnest(). If you want the sum per row in the original table:
SELECT (SELECT SUM(el.value)
FROM unnest(et.properties) el
WHERE el.name = 'revenue' -- or whatever the name is
) as TotalRevenue,
FROM deneme-319116.events.eventstable et;
If you want the overall sum over all rows:
SELECT SUM(el.value) as TotalRevenue,
FROM deneme-319116.events.eventstable et CROSS JOIN
unnest(et.properties) el
WHERE el.name = 'revenue' -- or whatever the name is

computed column base on ranked position

I am trying to get a new column(computed) to assign points based on the positions in the positions column as in this image
I have tried this query below but my quest was not successful: I seek your assistance please help
query in sql server
You can't do rank using apply like that. You'll always get "1". Use a subquery:
select . . .
from (select ae.*, rank() over (order by averagemark desc) as position
from agriculturalentries
) cross join
(values (case when rank >= 13 then 150 - rank * 10 end) ) as v(pointsearned);
I find the arithmetic easier to type than your case, but you can use the more verbose form.
You might ask why rank() always returns "1" in your query. That is because apply only considers one row at a time (as written). The rank over that row is necessarily "1".
With AllMarks AS
(SELECT
CompetitorID,
ApiEntryId,
AverageMark,
RANK() OVER (ORDER BY AverageMark)AS RankPosition
-- Add other columns
FROM ApicultureEntries
)
SELECT
a.*,(CASE WHEN a.RankPosition < 14 THEN 150 - RankPosition * 10 ELSE NULL END) AS PositionEarned
FROM AllMarks AS a

SQL percentage of the total

Hi how can I get the percentage of each record over the total?
Lets imagine I have one table with the following
ID code Points
1 101 2
2 201 3
3 233 4
4 123 1
The percentage for ID 1 is 20% for 2 is 30% and so one
how do I get it?
There's a couple approaches to getting that result.
You essentially need the "total" points from the whole table (or whatever subset), and get that repeated on each row. Getting the percentage is a simple matter of arithmetic, the expression you use for that depends on the datatypes, and how you want that formatted.
Here's one way (out a couple possible ways) to get the specified result:
SELECT t.id
, t.code
, t.points
-- , s.tot_points
, ROUND(t.points * 100.0 / s.tot_points,1) AS percentage
FROM onetable t
CROSS
JOIN ( SELECT SUM(r.points) AS tot_points
FROM onetable r
) s
ORDER BY t.id
The view query s is run first, that gives a single row. The join operation matches that row with every row from t. And that gives us the values we need to calculate a percentage.
Another way to get this result, without using a join operation, is to use a subquery in the SELECT list to return the total.
Note that the join approach can be extended to get percentage for each "group" of records.
id type points %type
-- ---- ------ -----
1 sold 11 22%
2 sold 4 8%
3 sold 25 50%
4 bought 1 50%
5 bought 1 50%
6 sold 10 20%
To get that result, we can use the same query, but a a view query for s that returns total GROUP BY r.type, and then the join operation isn't a CROSS join, but a match based on type:
SELECT t.id
, t.type
, t.points
-- , s.tot_points_by_type
, ROUND(t.points * 100.0 / s.tot_points_by_type,1) AS `%type`
FROM onetable t
JOIN ( SELECT r.type
, SUM(r.points) AS tot_points
FROM onetable r
GROUP BY r.type
) s
ON s.type = t.type
ORDER BY t.id
To do that same result with the subquery, that's going to be a correlated subquery, and that subquery is likely to get executed for every row in t.
This is why it's more natural for me to use a join operation, rather than a subquery in the SELECT list... even when a subquery works the same. (The patterns we use for more complex queries, like assigning aliases to tables, qualifying all column references, and formatting the SQL... those patterns just work their way back into simple queries. The rationale for these patterns is kind of lost in simple queries.)
try like this
select id,code,points,(points * 100)/(select sum(points) from tabel1) from table1
To add to a good list of responses, this should be fast performance-wise, and rather easy to understand:
DECLARE #T TABLE (ID INT, code VARCHAR(256), Points INT)
INSERT INTO #T VALUES (1,'101',2), (2,'201',3),(3,'233',4), (4,'123',1)
;WITH CTE AS
(SELECT * FROM #T)
SELECT C.*, CAST(ROUND((C.Points/B.TOTAL)*100, 2) AS DEC(32,2)) [%_of_TOTAL]
FROM CTE C
JOIN (SELECT CAST(SUM(Points) AS DEC(32,2)) TOTAL FROM CTE) B ON 1=1
Just replace the table variable with your actual table inside the CTE.

sql query constructing formula

i have a table with two column:
Name Values
----------------
Target 20
v1 10
v2 20
v3 10
Total 30
percent ?
i want to calculate the percentage with the single column value to get the formula as
--> Target/Total * 100.
i.e for ex: Total = SUM (Values) in that way......
by using two rows in the column in sql query, how to do calculation in single column with diff row values by implementing a formula in it.
i dont want to calculate total. i want to calculate percent:
formula:Target/Total * 100 . that gives me the value in percent row?
how? to query it?
You want an additional record to show up in your query output? That seems like you're trying to make it look & work like an Excel spreadsheet, but anyways, here's how you'd go about doing it:
SELECT Name, Values FROM table
UNION (Select function(inputs) AS Name, function(inputs) AS Values FROM table)
If you want to this in SQL...
;WITH
cTarget AS (SELECT Values AS Target FROM MyTable WHERE name = 'Target'),
cTotals AS (SELECT SUM(Values) AS Total FROM MyTable WHERE name <> 'Target')
SELECT * FROM MyTable
UNION ALL
SELECT 'Total', Total FROM cTotals
UNION ALL
SELECT
'Percentage', 1.0 * Target / Total * 100
FROM
cTarget
CROSS JOIN
cTotals