Call aliased column result of aggregate function JOOQ - postgresql

I'm currently trying to retrieve a single double value from this query in JOOQ Query Builder and PostgreSQL as the database, providing that DRINKS.PRICE is of type double and ORDER_DRINK.QTY is of type integer.
Record rec = create.select(DSL.sum(DRINKS.PRICE.multiply(ORDER_DRINK.QTY)).as("am_due")).from(ORDERS
.join(ORDER_DRINK
.join(DRINKS)
.on(DRINKS.DRINK_KEY.equal(ORDER_DRINK.DRINK_KEY)))
.on(ORDERS.ORDKEY.equal(ORDER_DRINK.ORDER_KEY)))
.where(ORDERS.TOKEN.eq(userToken))
.fetchOne();
As I've understood from the (brief) tutorial, once I retrieve the value from that aliased record, in the form:
double v = rec.getValue("am_due");
I should have the sum of all the prices multiplied by their quantities.
Still, I get a NullPointerException instead.
Any help would be very welcome.
Thank you.

Your jOOQ usage is correct, but I suspect that your sum is simply null because:
Your join doesn't return any records
All of your multiplication operands are null
and since you're unboxing Double to double, you're getting that NullPointerException. You can solve this
... using Java:
Double v1 = rec.getValue("am_due"); // will return null
double v2 = rec.getValue("am_due", double.class); // will return 0.0
... using jOOQ / SQL
Record rec = create.select(DSL.nvl(
DSL.sum(
DRINKS.PRICE.multiply(ORDER_DRINK.QTY)
), 0.0
).as("am_due"))

Related

How to catch null while summarizing records in EF6

next problem with Linq/EF6 queries.
I simply like to build a sum of some decimal fields:
var offsetHours1 = (from os in db.TimesheetOffsets
where (os.EmployeeId == employeeId && os.OffsetDate <= DateTime.Today)
select new
{
offset = os.OffsetHours
}).Sum(h=>h.offset);
So far it works, if I have records to sum but if the query returns null or no records, I get a System.InvalidOperationException
Is there an elegant way to summarize records in one step, so if there are no records, 0 is returned?
Thanks, Carsten
There's a quirk with the Sum extension method. As OffsetHours is a decimal, the overload of Sum you'll be using is Sum(..., decimal) which has this behaviour. To avoid it you can cast the value to a decimal? (nullable). With this you'll be using a different Sum that returns a nullable decimal and is OK with empty lists;
You can for example do this;
var offsetHours1 = (from os in db.TimesheetOffsets
where (os.EmployeeId == employeeId && os.OffsetDate <= DateTime.Today)
select os.OffsetHours)
.Sum(h => (decimal?)h);
Edit:
Removed unnecessary creation of anonymous type.

Explicit type conversion in postgreSQL

I am joining the two tables using the query below:
update campaign_items
set last_modified = evt.event_time
from (
select max(event_time) event_time
,result
from events
where request = '/campaignitem/add'
group by result
) evt
where evt.result = campaign_items.id
where the result column is of character varying type and the id is of integer type
But the data in the result column contains digits(i.e. 12345)
How would I run this query with converting the type of the result(character) into id
(integer)
Well you don't need to because postgresql will do implicit type conversion in this situation. For example, you can try
select ' 12 ' = 12
You will see that it returns true even though there is extra whitespace in the string version. Nevertheless, if you need explicit conversion.
where evt.result::int = campaign_items.id
According to your comment you have values like convRepeatDelay, these obviously cannot be converted to int. What you should then do is convert your int to char!!
where evt.result = campaign_items.id::char
There are several solutions. You can use the cast operator :: to cast a value from a given type into another type:
WHERE evt.result::int = campaign_items.id
You can also use the CAST function, which is more portable:
WHERE CAST(evt.result AS int) = campaign_items.id
Note that to improve performances, you can add an index on the casting operation (note the mandatory double parentheses), but then you have to use GROUP BY result::int instead of GROUP BY result to take advantage of the index:
CREATE INDEX i_events_result ON events_items ((result::int));
By the way the best option is maybe to change the result column type to int if you know that it will only contain integers ;-)

Lambda Expression selecting Max Numeric Value from a collection of AlphaNumeric Strings

I have this below SQL query that selects the Maximum integer value from a varchar field where the values start with MB.
select max( cast(substring( sLicenseNo, 4, len(sLicenseNo)) as int)) as licno from ApplicationForm where sLicenseNo like 'MB%'
How can I convert this lambda expression for use with entity framework ?
Try This
TestDataContext db = new TestDataContext();
var res = db.ApplicationForms.Where(y => y.sLicenseNo.Contains("MB")).Select(x => Convert.ToInt32(x.sLicenseNo.Substring(2))).Max();
Tell me if it works or not.

Trying to create aggregate function in PostgreSQL

I'm trying to create new aggregate function in PostgreSQL to use instead of the sum() function
I started my journey in the manual here.
Since I wanted to create a function that takes an array of double precision values, sums them and then does some additional calculations I first created that final function:
takes double precision as input and gives double precision as output
DECLARE
v double precision;
BEGIN
IF tax > 256 THEN
v := 256;
ELSE
v := tax;
END IF;
RETURN v*0.21/0.79;
END;
Then I wanted to create the aggregate function that takes an array of double precision values and puts out a single double precision value for my previous function to handle.
CREATE AGGREGATE aggregate_ee_income_tax (float8[]) (
sfunc = array_agg
,stype = float8
,initcond = '{}'
,finalfunc = eeincometax);
What I get when I run that command is:
ERROR: function array_agg(double precision, double precision[]) does
not exist
I'm somewhat stuck here, because the manual lists array_agg() as existing function. What am I doing wrong?
Also, when I run:
\da
List of aggregate functions
Schema | Name | Result data type | Argument data types | Description
--------+------+------------------+---------------------+-------------
(0 rows)
My installation has no aggregate functions at all? Or does only list user defined functions?
Basically what I'm trying to understand:
1) Can I use an existing functions to sum up my array values?
2) How can I find out about input and ouptut data types of functions? Docs claim that array_agg() takes any kind of input.
3) What is wrong with my own aggregate function?
Edit 1
To give more information and clearer picture of what I'm trying to achieve:
I have one huge query over several tables which goes something like this:
SELECT sum(tax) ... from (SUBQUERY) as foo group by id
I want to replace that sum function with my own aggregate function so I don't have to do additional calculations on backend - since they can all be done on database level.
Edit 2
Accepted Ants's answer. Since final solution comes from comments I post it here for reference:
CREATE AGGREGATE aggregate_ee_income_tax (float8)
(
sfunc = float8pl
,stype = float8
,initcond = '0.0'
,finalfunc = eeincometax
);
Array agg is an aggregate function not a regular function, so it can't be used as a state transition function for a new aggregate. What you want to do is to create an aggregate function which has a state transition function that is identical to array_agg and a custom final func.
Unfortunately the state transition function of array_agg is defined in terms of an internal datatype so it can't be reused. Fortunately there is an existing function in core that already does what you want.
CREATE AGGREGATE aggregate_ee_income_tax (float8)(
sfunc = array_append,
stype = float8[],
initcond = '{}',
finalfunc = eeincometax);
Also note that you had your types mixed up, you probably want aggregate a set of floats to an array, not a set of arrays to a float.
In addition to #Ants excellent advice:
1.) Your final function could be simplified to:
CREATE FUNCTION eeincometax(float8)
RETURNS float8 LANGUAGE SQL AS
$func$
SELECT (least($1, 256) * 21) / 79
$func$;
2.) It seems like you are dealing with money? In this case I would strongly advise to use the type numeric (preferred) or money for the purpose. Floating point operations are often not precise enough.
3.) The initial condition of the aggregate can simply be just 0:
CREATE AGGREGATE aggregate_ee_income_tax(float8)
(
sfunc = float8pl
,stype = float8
,initcond = 0
,finalfunc = eeincometax
);
4.) In your case (least(sum(tax), 256) * 21) / 79 is probably faster than your custom aggregate. Aggregate functions provided by PostgreSQL are written in C and optimized for performance. I would use that instead.

LINQ Query and DateTimes

I'm trying to get the Sum() from an Entityset<MyObject> with the next query.
(from MyObject p in SelectedObject.MyObjectEntitySet
where p.AColumn.HasValue &&
p.ADate >= dateTimeValue &&
p.ADate <= dateTimeValue2
select p.AColumn.Value).Sum();
with no luck retrieving correct sum.
Any Ideas?
[EDIT] OK WORKED!
You can troubleshoot the query by breaking it up into its constituent parts, and examining the output of each part:
var entitySet = SelectedObject.MyObjectEntitySet;
var entitySetWithValues = entitySet.Where(p => p.AColumn.HasValue);
var entitySetGreater = entitySetWithValues.Where(p => p.ADate >= DateTimeValue);
...and so forth.
If you see the list of overloads, the Sum method is over numeric values (decimal, float, int, double, long, etc). The selector has to be a function that takes a DateTime and returns a numeric value.
[edit]
Sorry, I didn't realize that AColumn was numeric.