Calculated field always returns 1 - atk 4.2 - atk4

I have a model that has a calculated field. The value returned is always 1. After turning the debug on, 1 is hardcoded in the query:
<?php class Model_Income extends Model_base_Income {
function init(){
parent::init();
$this->debug();
$this->addField('year')->calculated(true);
}
function calculate_year(){
return '22';
}
}
Query returned by debug
select `name`
,(select `name` from `client` where `income`.`client_id` = `client`.`id` )
`client`,`amount`,`date`,`comment`,1 `year`,`id`
from `income`
I am using atk 4.2

in 4.2 do so:
in model.
$this->add("Field_Expression", "year")->set("22");
or any sql query instead of 22.

Related

CakePHP how to get the running total in an entity using a virtual field?

I have field called deposit, I am trying to create a virtual field called balance. Below my desire output, It's like chaining sum.
deposit balance
100 100
300 400
10 410
I have tried below code in entity
public $balance = 0;
protected function _getBalance()
{
$this->balance = $this->balance + $this->deposit;
return $this->balance;
}
I have got all 0 in balance.
I am getting result like below
deposit balance
100 0
300 0
10 0
How can I get desire result ?
An entity has no idea about other entities, but that would be required in order for it to be able to sum up the balance.
The two solutions that come to my mind here are a) iterating over all the results and modifying the data, or b) in case your DBMS supports them, using window functions to create the running total on SQL level.
If you iterate over all results you can access the previous result's balance and calculate the sum and populate the balance field accordingly, for example in a result formatter:
$query->formatResults(function (\Cake\Collection\CollectionInterface $results) {
$previous = null;
return $results->map(function ($row) use (&$previous) {
if ($previous === null) {
$row['balance'] = $row['deposit'];
} else {
$row['balance'] = $previous['balance'] + $row['deposit'];
}
$previous = $row;
return $row;
});
});
On SQL level window functions would allow you sum up previous rows:
$query->select(function (\Cake\ORM\Query $query) {
return [
'deposit',
'balance' => $query
->func()
->sum('deposit')
->over()
->order('id')
->rows(null)
];
});
This would create a SELECT clause like this:
SELECT
deposit,
(
SUM(deposit) OVER (
ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)
) AS balance
where the sum is calculated over all previous rows up to and including the current row.
It should be noted that window functions on the builder are only supported as of CakePHP 4.1, in previous version you'd have to create custom expressions or pass raw SQL:
$query->select([
'deposit',
'balance' => 'SUM(deposit) OVER (
ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
)'
]);
See also
Cookbook > Database Access & ORM > Query Builder > Adding Calculated Fields
Cookbook > Database Access & ORM > Query Builder > Window Functions

EF Core 2 SQL scalar function value as part of insert/create

I am using EFCore 2.1 to create a record. I would like one field to be populated from a value generated from a scalar function.
public async Task<FraudClaim> CreateFraudClaimAsync(FraudClaim fraudClaim)
{
fraudClaim.AddedDate = DateTime.Now;
fraudClaim.UpdatedDate = DateTime.Now;
fraudClaim.Deleted = false;
fraudClaim.FraudReportNumber
=ScalarFunctionsHelpers.GetFraudReportNumber(); //Value From Scalar
Function
_dbcontext.Add(fraudClaim);
await _dbcontext.SaveChangesAsync().ConfigureAwait(false);
return fraudClaim;
}
The SQL that I would like generated would be similar to-
INSERT INTO [dbo].[FraudClaims]
([AddedDate]
,[UpdatedDate]
,[UpdatedBy]
,[Deleted]
,[FraudReportNumber]
,[WagersClaimNumber]
,[FraudNotificationDate]
)
VALUES
(getdate()
,getdate()
,'HD'
,0
, [dbo].[ufnGetFraudReportNumber]()
,1
,getdate()
)
I believe I can do this by breaking up the call the function to retrieve the value first then set the value but I was hoping to do it all in one trip.
Thanks for any suggestions.

Salesforce(apex) Query return values with toLowerCase

I using Salesforce (apex), i need Query that will select values from table and return them in toLowerCase.
some think like this:
//only example (not working code)
for(Users user:[select Name.toLowerCase(),LastName.toLowerCase() from Users ] )
{
//code....
}
For example if i have table Users with
Name | LastName
Boby | Testovich1
Dany | Testovich2
Ron | Testovich3
Query need to return me all values with toLowerCase:
boby testovich1,dany testovich2,ron testovich3
I can do this like this
for(Users user:[select Name,LastName from Users ] )
{
string UserName=user.Name.toLowerCase();
}
but is there a way to to this with querying?
Is there a way to do this in Salesforce (apex) Query ?
No you can't transform the return value to lower case in the query, you'll need to do it after you've gotten the query results.
One alternative is to add a formula field that returns the lower case value and query that instead.

Dapper and mssql using stored procedure returning meaningful error

I have a stored procedure inserts a row, and some conditions returns result set or single error code but when I use dapper return always same return class. so I couldn't understand If code gives me error or message rather than successful result set.
public static List<Result> Results(int Id)
{
using (IDbConnection connection = BL.DataProvider.OpenConnection())
{
return connection.Query<Result>("SearchResultGet", new { Id = Id }, commandType: CommandType.StoredProcedure).ToList();
}
}
ALTER PROCEDURE SearchResultGet
#Id int
AS
IF(id != 0)
SELECT * FROM XX WHERE Id = Id
ELSE
SELECT -1
codes are just sample, doesn't have any meaning.
There is no ORM/micro-ORM API that is going to like this; having a select -1 for one set of cases is just ... not pleasant. Options:
change the sproc to not do that - just run the regular select that returns zero rows
add the logic to the Results method instead (or in addition to) the sproc - i.e. check whether Id is zero in the C#
use a return -1 rather than a select -1 (although note that dapper doesn't make it trivial to capture return values)
use a sql error (raiserror)

SQL CLR Aggregate incorrectly returning null

I've added a user defined aggregate to my database for computing the product of a group.
The code was taken essentially verbatim from here.
I'm using the function to calculate the lifetime to date returns of financial instruments for which I have monthly return data. The table looks something like this:
----------------------------------------------------------
| InstrumentId(int) | MonthEnd(datetime) | Return(float) |
----------------------------------------------------------
My query looks like this:
SELECT R1.InstrumentId,
R1.MonthEnd,
R1.MonthlyReturn,
dbo.Product(1 + R2.MonthlyReturn) AS TotalReturn
FROM Returns R1
INNER JOIN Returns R2 ON R2.InstrumentId = R1.InstrumentId
AND R2.MonthEnd <= R1.MonthEnd
WHERE R1.InstrumentId BETWEEN 1 AND 50
GROUP BY R1.InstrumentId, R1.MonthEnd, R1.MonthlyReturn
ORDER BY R1.InstrumentId, R1.MonthEnd
The query works fine when I have only a few instruments, but adding certain instruments causes every result to be NULL. When I execute the query with OPTION(MAXDOP 1) the results are fine.
Does anyone know what's causing the issue?
EDIT:
Forgot to mention that I'm running SQL Server 2012 and the aggregate targets .NET 4.5
These are the modifications I would make to the Product aggregate if I wanted it to ignore NULLs.
Change the attribute:
[Microsoft.SqlServer.Server.SqlUserDefinedAggregate(
Microsoft.SqlServer.Server.Format.Native,
IsInvariantToDuplicates = false,
IsInvariantToNulls = true, // receiving a NULL value will be ignored
IsInvariantToOrder = true,
IsNullIfEmpty = true,
Name = "Product"
)]
Change Accumulate:
public void Accumulate(System.Data.SqlTypes.SqlDouble number) {
if (!this.HasValue && !number.IsNull) { //Don't know if we'll be passed a NULL, but protect ourselves nonetheless
this.Result = number;
} else if (number.IsNull) {
return; //Avoid setting HasValue
} else {
this.Result = System.Data.SqlTypes.SqlDouble.Multiply(this.Result, number);
}
this.HasValue = true;
}
Change Merge:
public void Merge(Product group) {
if (group.HasValue) {
if(this.HasValue) {
this.Result = System.Data.SqlTypes.SqlDouble.Multiply
(this.Result, group.Result);
} else { //We may never have had our own value set
this.Result = group.Result;
this.HasValue = true;
}
}
}
I'm not sure if the change to Merge is truly needed, but I'd do it for safety's sake.
If 1 + R2.MonthlyReturn is positive, I would considered use of exp(sum(log(...))) equivalent:
SELECT R1.InstrumentId,
R1.MonthEnd,
R1.MonthlyReturn,
EXP(SUM(LOG(1 + R2.MonthlyReturn))) AS TotalReturn
FROM Returns R1
INNER JOIN Returns R2 ON R2.InstrumentId = R1.InstrumentId
AND R2.MonthEnd <= R1.MonthEnd
WHERE R1.InstrumentId BETWEEN 1 AND 50
GROUP BY R1.InstrumentId, R1.MonthEnd, R1.MonthlyReturn
ORDER BY R1.InstrumentId, R1.MonthEnd

Resources