How to implement width_bucket function in a single query - sql

Oracle SQL provides
width_bucket(expression,min_value,max_value,num_buckets)
function to create a histogram. WIDTH_BUCKET Oracle SQL Reference. I want to know if the same functionality can be achieved using a nested query or something ?
Update: If it is not possible through a single query, I would like to know which of the following methods to implement the histogram function would be fastest in performance?
SQL PL stored procedure
JAVA stored procedure
JDBC program

WIDTH_BUCKET ( e, m, M, p ) is an ISO SQL function that can be convert in a simple mathematic formulae...
The strict equivalent in "plain" SQL is :
CASE
WHEN e < m
THEN 0
ELSE 1 + FLOOR((e - m ) / ((M - m)/p))
END

Related

Issue While Creating Product of All Values Of Column (UDF in Snowflake)

I was trying to create a Snowflake SQL UDF
Where it computes the Values of the all values and will return the result to the user.
So firstly, i have tried the following approach
# The UDF that Returns the Result.
CREATE OR REPLACE FUNCTION PRODUCT_OF_COL_VAL()
RETURNS FLOAT
LANGUAGE SQL
AS
$$
SELECT EXP(SUM(LN(COL))) AS RESULT FROM SCHEMA.SAMPLE_TABLE
$$
The above code executes perfectly fine....
if you could see above (i have hardcoded the TABLE_NAME and COLUMN_VALUE) which is not i acutally want..
So, i have tried the following approach, by passing the column name dynamically..
create or replace function (COL VARCHAR)
RETURNS FLOAT
LANGUAGE SQL
AS
$$
SELECT EXP(SUM(LN(COL))) AS RESULT from SCHEMA.SAMPLE_TABLE
$$
But it throws the following issue...
Numeric Value 'Col' is not recognized
To elaborate more the Data type of the Column that i am passing is NUMBER(38,6)
and in the background its doing the following work..
EXP(SUM(LN(TO_DOUBLE(COL))))
Does anyone have any idea why this is running fine in Scenario 1 and not in Scenario 2 ?
Hopefully we will be able to have this kind of UDFs one day, in the meantime consider this answer using ARRAY_AGG() and a Python UDF:
Sample usage:
select count(*) how_many, multimy(array_agg(score)) multiplied, tags[0] tag
from stack_questions
where score > 0
group by tag
limit 100
The UDF in Python - which also protects against numbers beyond float's limits:
create or replace function multimy (x array)
returns float
language python
handler = 'x'
runtime_version = '3.8'
as
$$
import math
def x(x):
res = math.prod(x)
return res if math.log10(res)<308 else 'NaN'
$$
;
The parameter you defined in SQL UDF will be evaluated as a literal:
When you call the function like PRODUCT_OF_COL_VAL('Col'), the SQL statement you execute becomes:
SELECT EXP(SUM(LN('Col'))) AS RESULT from SCHEMA.SAMPLE_TABLE
What you want to do is to generate a new SQL based on parameters, and it's only possible using "stored procedures". Check this one:
Dynamic SQL in a Snowflake SQL Stored Procedure

Get length of oracle.sql.array

On an Oracle DB I have a table with SDO_GEOMETRY objects. I would like to query the database for those polygons with less than x edges. In theory this would be easy with a query like
SELECT * FROM myTable t WHERE LENGTH(t.geometry.sdo_ordinates) < x
Obviously the LENGTH funtion is defined for char and the type of
t.geometry.sdo_ordinates is oracle.sql.ARRAY so that doesn't work. Shouldn't there be a trivial way to SELECT the length or an array in Oracle? Somehow I'm unable to get the syntax right.
PS: I kind of solved my search with the following query, still the original questerion remains, isn't there an array size/length function?
SELECT * FROM myTable t WHERE LENGTH(t.geomety.Get_WKT()) < (x * c)
No, there is no simple sql function that counts the elements of an array.
However as mentioned here, another idea is a PL/SQL script.
create or replace function get_count(ar in SDO_ORDINATE_ARRAY) return number is
begin
return ar.count;
end get_count;
t.geometry.sdo_ordinates.COUNT is a PL/SQL attribute that can be used within functions/procedures. Thus that is not a function useable in plain SQL.
Attribute:
value.someAttribute
Function:
doSomething(value)
Clarification: Functions have return values, procedures don't. Source

Syntax Errors in User Defined Function in SQL

I'm unable to understand why this code to calculate the Volume of a cube results in the Error Procedure or function 'CubicVolume' expects parameter '#CubeLength', which was not supplied..
CREATE FUNCTION CubicVolume
-- Input dimensions in centimeters
(
#CubeLength decimal(4,1),
#CubeWidth decimal(4,1),
#CubeHeight decimal(4,1)
)
RETURNS decimal(12,3)
AS
BEGIN
RETURN(#CubeLength * #CubeWidth * #CubeHeight)
END
I then try to Execute it using EXEC CubicVolume, which is how one would Execute a Stored Procedure.
I know some Syntax is wrong, but I'm unable to tell where.
Thank You
I'm assuming SQL Server.
You defined a scalar function, but are trying to invoke it like a stored procedure. The two are not the same. Stored procedures are basically batches of SQL statement that execute sequentially, and optionally send resultsets back to the client. Scalar functions behave like built-in functions (e.g., LEN(), CHARINDEX(), ABS(), etc.). That is, they belong in an expression inside a SELECT statement, or moral equivalent. So you need to use your function in a SELECT statement where you'd use a column or an expression. Examples:
SELECT dbo.CubicVolume(12, 5, 3) AS vol
-- result is: 180
SELECT CubeName, l, w, h FROM dbo.SomeTable WHERE dbo.CubicVolume(l, w, h) > 200
-- result could be something like: MyCube, 10, 10, 10 etc.
Basically, it's equivalent to using a mathematical expression based on columns or constants. You cannot use EXEC on it.

SQL Server - evaluate a function in a dynamic query

I have a piece of dynamic SQL inside part of which retrieves a function dependent on other results from the query, but also uses these results to evaluate this function. I know eval() does not exist in SQL so what do I use?
A very simplified version
select reading, functiontype, #result = eval(f.functionformula)
from readingstables r
join functiontable f on (r.functiontype = f.functiontype)
So basically (note these are only example formulae) I want to use the functionformula which is related to a set of readings via the formulatype
if f.functiontype == 'A' then f.functionformula = reading * reading
if f.functiontype == 'B' then f.functionformula = reading * costant / anothervalue
//etc etc
The real version is a huge piece of dynamic SQL in a stored procedure that drives a cursor. I would prefer to do it in one query but suspect I might have to compromise and have a second dynamic query driven from the first.
Why not simply use the POWER function:
Case functionType
When 'A' Then Power( reading, 2 )
When 'B' Then Power( reading, 3 )
...
End
You could even get super fancy like so:
Power( reading, Ascii( functionType ) - Ascii('A') + 2 )
Edit
Given your change to your OP, beyond dynamic SQL, there is no way to dynamically execute a function call. You could create a UDF which takes the function type parameter and executes the correct expression however the UDF itself would need to be a large Case expression.
Create Function FunctionTypeExpression( #FunctionType char(1) )
Returns float
As
Return Case #FunctionType
When 'A' Then ..expression 1
When 'B' Then ..expression 2
...
One note in this, you will need to make the return value of the function compatible with any possible return type from the expressions. Hopefully, they are all numeric. If they are not all numeric (or all text), then a more detailed explanation for why this is not the case would be needed.

Boolean expression as column value in transact sql

In most RDBMS:es, this work:
select (5 > 3)
and evaluates to true. It doesn't work in MS Transact SQL and the only workaround I've found is to write:
select case when 5 > 3 then 1 else 0 end
Which kind of sucks because it is much more verbose. Is there a better way to write the above kind of checks?
If the problem is arithmetic comparison:
select (5 - 3)
Then at the application level test for < or = or > 0.
You could write it as a scalar-valued function, but it will be very slow on large datasets.
If your program often requires such case constructs you could create your set of functions that will have user functions like Bool_IsGreater(left, right) that will return you your desired 0 or 1.
SQL Server doesn't support boolean value type anyway even for basic column use.
If you will need performance and those 5 and 3 values come naturally from some select query you might want to create a new column and set its value to 1 or 0 by trigger or something, which could help with performance.