I'm starting to use EntityFramework 6 with Oracle (client 12.x).
I have severals tables with DATE columns and TIMESTAMP columns.
All queries generated by EF6 convert .NET DateTime type to TIMESTAMP. So the query performance is very poor.
I tried to add a precision to 0 for the DATETIME columns but nothing changes on the generated query.
Property(_ => _.MyDate).HasColumnName("DATE_COLUMN").HasPrecision(0);
How can I specify in my code to translate my where clause by a TO_DATE function and not by a TO_TIMESTAMP function ?
I've had a similar issue with DATE Oracle type and EF6.1.
My workaround was to use the CodeFirstFunctions library (available only for EF6.1 and later) and specify a conversion function first in Oracle:
create or replace FUNCTION TO_DATE_LOCAL
(
DATETIMETOCONVERT IN VARCHAR2
, CONVERTFORMAT IN VARCHAR2
) RETURN DATE AS
BEGIN
RETURN to_date(DATETIMETOCONVERT, CONVERTFORMAT);
END TO_DATE_LOCAL;
And later in my DbContext:
[DbFunction("CodeFirstDatabaseSchema", "TO_DATE_LOCAL")]
public static DateTime ToDateLocal(string dateTimeToConvert, string convertFormat)
{
// no need to provide an implementation
throw new NotSupportedException();
}
So I can force Entity Framework to use DATE type in a where condition:
var measurement =
context.Measurements
.Where(m => m.MeasuredAt ==
PlantContext.ToDateLocal("2016.01.01 10:00:00", "YYYY.MM.DD Hh24:MI:SS"))
.FirstOrDefault();
Be careful to use capital letters for the function name and for the schema name with CodeFirstFunctions if you use Oracle.
If you need more details I've written a blog post about this with an example project.
Related
I'm trying to use JPA Criteria API to filter a date between a specific range.
The problem is that I don't have directly the date in my entity, I obtain the date using a date field and adding a number of days (which are in another field) to this date .
I look around and I saw some responses which recommends to calculate the date in java before pass it to criteria builder, but the problem is that I don't know the number of days to add because it depends on the days in a specific column of the table.
The simplified table OBJECT has a field CREATION_DATE of type date and EXPIRATION_DAYS which is a number. I want to filter by a range of possible expiration date, I get the expiration date of each element in OBJECT table adding EXPIRATION_DAYS to CREATION_DATE, the SQL query is:
select * from OBJECT
where CREATION_DATE + EXPIRATION_DAYS
between to_date('01/01/2018','dd/MM/yyyy') and to_date('01/02/2018','dd/MM/yyyy')
In CriteriaBuilder I make some attempts, but I think the best approach I try to use is the follow:
CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery<Object> criteria = criteriaBuilder.createQuery(Object.class);
List<Predicate> predicates...
predicates.add(criteriaBuilder.between(
criteriaBuilder.function("AddDate", Date.class, Object.get("creationDate"), criteriaBuilder.literal("day"), Object.get("expirationDays")),
dateInit, dateEnd));
criteria.where(predicates.toArray(new Predicate[]{}));
...
TypedQuery<Object> query = entityManager.createQuery(criteria);
return query.getResultList();
I use function("addDate"...) method, the problem is that I thought JPA implementation translate "addDate" to specific database vendor but it's not, because it's throwing the following exception which is pretty clear:
javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.6.4.v20160829-44060b6): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: java.sql.SQLSyntaxErrorException: ORA-00904: "ADDDATE": invalid identifier
And the query trace:
(AddDate(t0.CREATION_DATE, ?, t0.EXPIRATION_DAYS) BETWEEN ? AND ?)).
How can I achieve this using ORACLE as a database? Or there is a better approach?
In Hibernate seems that there is the #Formula annotation which provides the functionality I'm looking for, however there is no such thing in JPA neither in specific implementation I'm using(eclipselink).
I want to avoid the necessity of declare a specific function, but unfortunately I do it this way, first I declare the function ADD_DAYS in Oracle as follows:
create or replace function ADD_DAYS(dateValue DATE, daysValue NUMBER)
return DATE is
begin
return dateValue + daysValue;
end;
Then I call this function using function method of criteriaBuilder, and passing as parameters the function name, the type of object returned by the oracle function, and the two arguments I define, the column with the date, and the column with the days to add:
criteriaBuilder.function("ADD_DAYS", Date.class, Object.get("creationDate"), Object.get("expirationDays"))
I've written a stored procedure that updates a table.
But I would like to take into account where one or more of the parameters are NULL.
In such an instance, I don't want to update the column, I want to leave the existing value as is.
I've tried to use:
UPDATE
VS_USER_T
SET
USR_FIRST_NAME = ISNULL(p_NewUsrFName, #p_NewUsrFName)
WHERE
USR_ID = lv_Num_UsrId;
But I get an error on the '#', I'm using Oracle 12c.
This is the procedure call
PROCEDURE UpdateUser
( p_UserId IN VS_USER_T.USR_ID%TYPE,
p_NewUsrFName IN VS_USER_T.USR_FIRST_NAME%TYPE,
p_NewUsrLName IN VS_USER_T.USR_LAST_NAME%TYPE,
p_NewUsrname IN VS_USER_T.USR_LOGIN%TYPE)
Please advise how my UPDATE statement should look like, when 'p_NewUsrname ' can be NULL, in which case I want to leave the existing value as is.
Thanks in advance.
To keep the existing value you need to refer to the existing column value:
USR_FIRST_NAME = ISNULL(p_NewUsrFName, USER_FIRST_NAME)
or you could use:
USR_FIRST_NAME = CASE WHEN p_NewUsrFName is null THEN USER_FIRST_NAME ELSE NewUsrFName END
ISNULL() is not yet a standard Oracle function (at least in the Oracle 12c version that you say you are using). If is of course possible to write a PL/SQL function called ISNULL() and use that.
For a standard Oracle 12c installation, try using NVL or COALESCE instead.
USR_FIRST_NAME = NVL(p_NewUsrFName, USR_FIRST_NAME)
or
USR_FIRST_NAME = COALESCE(p_NewUsrFName, USR_FIRST_NAME)
You could use a decode statement e.g.
update my_table t
set username = decode(p_NewUsrname, NULL, t.username, p_NewUsrname)
where t.id = p_UserId;
I am writing an SQL query where the query should first search the first value, and only if this value is missing the query should search for the second value.
I have two tables. One of these tables contains the modification date (this is not always filled and can be null) and a creation date which is always filled.
Now what I want is that the query first looks in the table with the modification date and only if it is null looks at the table with the creation date.
Example of the query:
Select *
from all_articles
where to_char(modification_date, 'YYYYMMDD') = to_char(sysdate, 'YYYYMMDD')-1
-- if this is an empty record then
to_char(creation_date, 'YYYYMMDD') = to_char(sysdate, 'YYYYMMDD')-1
Can anyone help me with this query?
Almost all the major RDBMS' available have in built functions to handle such a situation.
The Oracle DB has NVL function which works as follows:
NVL(Modified_dt, Create_dt);
The above will return Modified_dt column data by default. However, if that isn't available, it will return Create_dt.
See here for details:
http://www.w3schools.com/sql/sql_isnull.asp
I have a birt dataset for a db2 query. My query works fine without parameters with the following query...
with params as (SELECT '2014-02-16' enddate,'1' locationid FROM sysibm.sysdummy1)
select
t.registerid
from (
select
...
FROM params, mytable sos
WHERE sos.locationid=params.locationid
AND sos.repositorytype ='xxx'
AND sos.repositoryaccountability='xxx'
AND sos.terminalid='xxx'
AND DATE(sos.balanceDate) between date(params.enddate)-6 DAY and date(params.enddate)
GROUP BY sos.terminalid,sos.balancedate,params.enddate) t
GROUP BY
t.registerid
WITH UR
But when I change the top line to ...
with params as (SELECT ? enddate,? locationid FROM sysibm.sysdummy1)
And make the two input paramters of string datatype I get db2 errors sqlcode -418. But i know that it is not my querty because my query works.
What is the right way for me to set up the parameters so there is no error?
thanks
I'm not familiar with DB2 programming, but on Oracle the ? works anywhere in the query.
Have you looked at http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=%2Fcom.ibm.db2z9.doc.codes%2Fsrc%2Ftpc%2Fn418.htm?
Seems that on DB2 it's a bit more complicated and you should use "typed parameter markers".
The doc says:
Typed parameter marker
A parameter marker that is specified with its target data type. A typed parameter marker has the general form:
CAST(? AS data-type)
This invocation of a CAST specification is a "promise" that the data type of the parameter at run time will be of the data type that is specified or some data type that is assignable to the specified data type.
Apart from that, always assure that your date strings are in the format that the DB expects, and use explicit format masks in the date function, like this:
with params as (
SELECT cast (? as varchar(10)) enddate,
cast (? as varchar2(80)) locationid
FROM sysibm.sysdummy1
)
select
...
from params, ...
where ...
AND DATE(sos.balanceDate) between date(XXX(params.enddate))-6 DAY and date(XXX(params.enddate))
...
Unfortunately I cannot tell you how the XXX function should look on DB2.
On Oracle, an example would be
to_date('2014-02-18', 'YYYY-MM-DD')
On DB2, see Converting a string to a date in DB2
In addition to hvb answer, i see two options:
Option 1 you could use a DB2 stored procedure instead of a plain SQL query. Thus there won't be these limitations you face to, due to JDBC query parameters.
Option 2, we should be able to remove the first line of the query "with params as" and replace it with question marks within the query:
select
t.registerid
from (
select
sos.terminalid,sos.balancedate,max(sos.balanceDate) as maxdate
FROM params, mytable sos
WHERE sos.locationid=?
AND sos.repositorytype ='xxx'
AND sos.repositoryaccountability='xxx'
AND sos.terminalid='xxx'
AND DATE(sos.balanceDate) between date(?)-6 DAY and date(?)
GROUP BY sos.terminalid,sos.balancedate) t
GROUP BY
t.registerid
A minor drawback is, this time we need to declare 3 dataset parameters in BIRT instead of 2. More nasty, i removed params.endDate from "group by" and replaced it with "max(sos.balanceDate)" in select clause. This is very near but not strictly equivalent. If this is not acceptable in your context, a stored procedure might be the best option.
I'm working on a Report in Oracle Reports Builder, and having issues with my AFTERPFORM trigger. When viewing the report, it seems like the year is being processed properly, while the period and subperiod are using their initial values. I'm confused because they are the exact same select statement, but are not working as intended. Any help would be greatly appreciated!
function AfterPForm return boolean is
v_subpdenddt_user date;
v_subpdenddt_max date;
v_rowcount integer;
begin
select value into year from wos_report_param where parameter = 'year' and sequence_num = :sequencenum;
select value into period from wos_report_param where parameter = 'period' and sequence_num = :sequencenum;
select value into user_subpd from wos_report_param where parameter = 'subpd' and sequence_num = :sequencenum;
you have to check variable datatypes are they matched the database columns?
try to change the above code to work with varchar2 variables, then convert varchar2 to date if you want to deal with dates, or to numbers if you want to deal with numbers.