HQL Date throwing unexpected token - sql

Hi I have trouble executing the following query in HQL:

This is a very dangerous approach to running queries. It's the sort of thing that creates SQL injection risks. Never concatenate values into SQL or HQL strings like that. Always use the PreparedStatement approach of using placeholders in the SQL/HQL string and setting the values programmatically. This way the driver (and Hibernate in the case of HQL) can do the correct thing with the SQL that gets generated. Of course here the value is a date and not a user-submitted string, but the principle still holds.
What you need to do is run a query more like
'select stuff from bar b where b.dateCreated = ?'
In HQL you can also use named parameters, and those are usually a lot easier to read and self-documenting, e.g.
'select stuff from bar b where b.dateCreated = :date'
Then set the value as part of the call, not with string concatenation.
The problem here is that the Java/Groovy toString value of a date is nothing at all like what the date format should be in SQL (or HQL). Luckily you don't need to know what that format should be, because the JDBC driver does.
So your query should be
def co = Bid.executeQuery(
'select b.job from Bid b left join b.job j where j.dateCreated = :date',
[date: fromDates[i])
Here I'm using the name date but that's arbitrary, is just has to match the key name in the map with values. You can also use SQL-style ? chars and a list of values:
def co = Bid.executeQuery(
'select b.job from Bid b left join b.job j where j.dateCreated = ?',
[fromDates[i])
Once you get this working you'll find that comparing dates like this rarely works because the dates have to agree to the millisecond, but that's a problem for another question :)

Related

Why does OLEDB (or Access?) rewrite WHERE clause

I'm facing a strange situation where OledbDataAdapter rearranges the WHERE clause of my query, thus shuffling all parameters. In addition, it doesn't recognize one of the parameters used in the query. Here's how it goes:
A simple table with 4 columns (and infinite rows :)):
Name varchar(50)
Category varchar(20) //One of around 15-20 possible values
YR int //Year
Period int //Month
User will query this table using Year, Month and a comma-separated list of categories that he's interested in. Since .NET doesn't support multi-valued parameters through IN operator, what I do is to accept the list of categories as comma-separated list and then prepend and append a comma to this list within the query and use built-in INSTR() function to filter results for desired categories. User can also supply an empty string for categories filter in which case query will need to return all results (i.e. no filter on categories).
Therefore my query looks like this:
SELECT * FROM [Table1] WHERE
YR = ? AND
Period = ? AND
(LTRIM(RTRIM(?)) = '' OR INSTR(1, ?, ',' + Category + ',') > 0)
This worked with MDBs in the past. But recently I tried it against an ACCDB. The first thing I noted is that when I try to run the query in OledbDataAdapter wizard, Visual Studio rewrites it as:
SELECT * FROM [Table1] WHERE
(YR = ? AND Period = ? AND LTRIM(RTRIM(?)) = '') OR
(YR = ? AND Period = ? AND INSTR(1, ?, ',' + Category + ',') > 0)
In other words, it has rearranged the condition A AND B AND (C OR D) as (A AND B AND C) OR (A AND B AND D).
This would have been fine with me, but the additional problem is that this changes the number of parameters from 4 to 6; the query therefore doesn't return any results even when it should. Moreover, it doesn't recognize that last parameter (the question mark within INSTR function) at all.
So my questions are:
Who is rewriting the query (OledbDataAdapter, Access query parser or something else)?
Why is it doing so? Optimization? Apparently my version of the WHERE clause is more efficient.
Why doesn't it see the last parameter?
How to fix/workaround this issue? (plz do not suggest using 2 separate queries for it).

Pentaho Dynamic SQL queries

I have a Pentaho CDE project in development and i wanted to display a chart wich depends on several parameters (like month, year, precise date, country, etc). But when i want to "add" another parameter to my query, it doesn't work anymore... So i'm sure i'm doing something wrong but what ? Please take a look for the parameter month for example :
Select_months_query : (this is for my checkbox)
SELECT
"All" AS MONTH(TransactionDate)
UNION
SELECT DISTINCT MONTH(TransactionDate) FROM order ORDER BY MONTH(TransactionDate);
Select_barchart_query : (this is for my chart, don't mind the other tables)
SELECT pginit.Family, SUM(order.AmountEUR) AS SALES
FROM pginit INNER JOIN statg ON pginit.PG = statg.PGInit INNER JOIN order ON statg.StatGroup = order.StatGroup
WHERE (MONTH(order.TransactionDate) IN (${month}) OR "All" IN (${month}) OR ${month} IS NULL) AND
/*/* Apply the same pattern for another parameter (like year for example) *\*\
GROUP BY pginit.Family
ORDER BY SALES;
(Here, ${month} is a parameter in CDE)
Any ideas on how to do it ?
I read something there that said to use CASE clauses... But how ?
http://forums.pentaho.com/showthread.php?136969-Parametrized-SQL-clause-in-CDE&highlight=dynamic
Thank you for your help !
Try simplifying that query until it runs and returns something and work from there.
Here are some things I would look into as possible causes:
I think you need single quotes around ${parameter} expressions if they're strings;
"All" should probably be 'All' (single quotes instead of double quotes);
Avoid multi-line comments. I don't think you can have multi-line comments in CDE SQL queries, although -- for single line comments usually works.
Be careful with multi-valued parameters; they are passed as arrays, which CDA will convert into comma separated lists. Try with a single valued parameter, using = instead of IN.

Using the '?' Parameter in SQL LIKE Statement

I'm accessing a Firebird database through Microsoft Query in Excel.
I have a parameter field in Excel that contains a 4 digit number. One of my DB tables has a column (TP.PHASE_CODE) containing a 9 digit phase code, and I need to return any of those 9 digit codes that start with the 4 digit code specified as a parameter.
For example, if my parameter field contains '8000', I need to find and return any phase code in the other table/column that is LIKE '8000%'.
I am wondering how to accomplish this in SQL since it doesn't seem like the '?' representing the parameter can be included in a LIKE statement. (If I write in the 4 digits, the query works fine, but it won't let me use a parameter there.)
The problematic statements is this one: TP.PHASE_CODE like '?%'
Here is my full code:
SELECT C.COSTS_ID, C.AREA_ID, S.SUB_NUMBER, S.SUB_NAME, TP.PHASE_CODE, TP.PHASE_DESC, TI.ITEM_NUMBER, TI.ITEM_DESC,TI.ORDER_UNIT,
C.UNIT_COST, TI.TLPE_ITEMS_ID FROM TLPE_ITEMS TI
INNER JOIN TLPE_PHASES TP ON TI.TLPE_PHASES_ID = TP.TLPE_PHASES_ID
LEFT OUTER JOIN COSTS C ON C.TLPE_ITEMS_ID = TI.TLPE_ITEMS_ID
LEFT OUTER JOIN AREA A ON C.AREA_ID = A.AREA_ID
LEFT OUTER JOIN SUPPLIER S ON C.SUB_NUMBER = S.SUB_NUMBER
WHERE (C.AREA_ID = 1 OR C.AREA_ID = ?) and S.SUB_NUMBER = ? and TI.ITEM_NUMBER = ? and **TP.PHASE_CODE like '?%'**
ORDER BY TP.PHASE_CODE
Any ideas on alternate ways of accomplishing this query?
If you use `LIKE '?%', then the question mark is literal text, not a parameter placeholder.
You can use LIKE ? || '%', or alternatively if your parameter itself never contains a LIKE-pattern: STARTING WITH ? which might be more efficient if the field you're querying is indexed.
You can do
and TP.PHASE_CODE like ?
but when you pass your parameter 8000 to the SQL, you have to add the % behind it, so in this case, you would pass "8000%" to the SQL.
Try String Functions: Left?
WHERE (C.AREA_ID = 1 OR Left(C.AREA_ID,4) = "8000")

NHibernate RowCount in LINQ produces SQL Exception

I have a query where I am only interested in the row count, however the query that NHibernate produces does not work with Sybase. I already have a custom Sybase dialect, but I can't find where to override the rowcount.
Given the following code:
var a = from b in table where b.something = 5 select b
var rows = a.Count
Generates an SQL similar to this:
select cast(count(*) as INTEGER) as p1 from table
I don't get why NHibernate wants to cast the count result, nor how I can override the dialect or elsewhere so NHibernate doesn't include the cast. The result of a count is castable to integer anyways.
If I however use QueryOver, things work perfectly. The problem then however, is that one off my conditions is dependent on the length of a string (yes, the db design could be better, but I can currently not change it). Using linq to call .Length on a string in the conditions work. However I can't use the string length as a condition in the QueryOver expressions. I also need a contains operation, which works with linq, but not QueryOver.
Is there a way to override how the Count query is generated, so it will work?
I am only interested if there is any rows matching, not the count, is there a different way of doing that?
Can instead the QueryOver? interface to use the SQL length and in operators?
You can understand if there are any rows matching by using Any function like this:
var a = from b in table where b.something = 5 select b;
var isMatch = a.Any();

SQL produced by Entity Framework for string matching

Given this linq query against an EF data context:
var customers = data.Customers.Where(c => c.EmailDomain.StartsWith(term))
You’d expect it to produce SQL like this, right?
SELECT {cols} FROM Customers WHERE EmailDomain LIKE #term+’%’
Well, actually, it does something like this:
SELECT {cols} FROM Customer WHERE ((CAST(CHARINDEX(#term, EmailDomain) AS int)) = 1)
Do you know why?
Also, replacing the Where selector to:
c => c.EmailDomain.Substring(0, term.Length) == term
it runs 10 times faster but still produces some pretty yucky SQL.
NOTE: Linq to SQL correctly translates StartsWith into Like {term}%, and nHibernate has a dedicated LikeExpression.
I don't know about MS SQL server but on SQL server compact LIKE 'foo%' is thousands time faster than CHARINDEX, if you have INDEX on seach column. And now I'm sitting and pulling my hair out how to force it use LIKE.
http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/1b835b94-7259-4284-a2a6-3d5ebda76e4b
The reason is that CharIndex is a lot faster and cleaner for SQL to perform than LIKE. The reason is, that you can have some crazy "LIKE" clauses. Example:
SELECT * FROM Customer WHERE EmailDomain LIKE 'abc%de%sss%'
But, the "CHARINDEX" function (which is basically "IndexOf") ONLY handles finding the first instance of a set of characters... no wildcards are allowed.
So, there's your answer :)
EDIT: I just wanted to add that I encourage people to use CHARINDEX in their SQL queries for things that they didn't need "LIKE" for. It is important to note though that in SQL Server 2000... a "Text" field can use the LIKE method, but not CHARINDEX.
Performance seems to be about equal between LIKE and CHARINDEX, so that should not be the reason. See here or here for some discussion. Also the CAST is very weird because CHARINDEX returns an int.
charindex returns the location of the first term within the second term.
sql starts with 1 as the first location (0 = not found)
http://msdn.microsoft.com/en-us/library/ms186323.aspx
i don't know why it uses that syntax but that's how it works
I agree that it is no faster, I was retrieving tens of thousands of rows from our database with the letter i the name. I did find however that you need to use > rather than = ... so use
{cols} FROM Customer WHERE ((CAST(CHARINDEX(#term, EmailDomain) AS int)) > 0)
rather than
{cols} FROM Customer WHERE ((CAST(CHARINDEX(#term, EmailDomain) AS int)) = 1)
Here are my two tests ....
select * from members where surname like '%i%' --12 seconds
select * from sc4_persons where ((CAST(CHARINDEX('i', surname) AS int)) > 0) --12 seconds
select * from sc4_persons where ((CAST(CHARINDEX('i', surname) AS int)) = 1) --too few results