Java PreparedStatement can't identify placeholders in subquery - java

When using a Java PreparedStatement, the question-mark placeholders aren't being detected. It would throw an error "The column index is out of range: 1, number of columns: 0" when invoking statementName.setLong(1, 123). My example is from Postgres 8.4, but the problem occurs before the SQL has a chance to make it to the SQL server.
After comparing against some working prepared statements, I realized that the broken one contained a subquery similar to:
SELECT * FROM (
SELECT DISTINCT (name)
id,
name
FROM MyTable
WHERE id > ?
ORDER BY name) AS Level1
ORDER BY 1

The solution that worked for me was to convert the query to a CTE (Common Table Expression). The revised query looks like this:
WITH Level1 AS (
SELECT DISTINCT (name)
id,
name
FROM MyTable
WHERE id > ?
ORDER BY name)
SELECT *
FROM Level1
ORDER BY 1

In JDBC, the parameter indexes for prepared statements begin at 1 instead of 0.

Related

How can I access a value when inserting into a table?

I'm trying to write a java sql query, the simplified table would be table(name,version) with a unique constraint on (name, version).
I'm trying to insert a row into my database with a conditional statement. Meaning that when a entry with the same name exists, it should insert the row with same name and its version increased by 1.
I have tried with the following:
INSERT INTO table(name,version)
VALUES(?, CASE WHEN EXISTS(SELECT name from table where name=?)
THEN (SELECT MAX(version) FROM table WHERE name = ?) +1
ELSE 1 END)
values are sent by user.
My question is, how can I access the 'name' inside the values so I could compare them?
If you want to write this as a single query:
INSERT INTO table (name, version)
SELECT ?, COLAESCE(MAX(t2.version) + 1, 1)
FROM table t2
WHERE t2.name = ?;
That said, this is dangerous. Two threads could execute this query "at the same time" and possibly create the same version number. You can prevent this from happening by adding a unique index/constraint on (name, version).
With the unique index/constraint, one of the updates will fail if there is a conflict.
I see at least two approaches:
1. For each pair of name and version you first query the max version:
SELECT MAX(VERSION) as MAX FROM <table> WHERE NAME = <name>
And then you insert the result + 1 with a corresponding insert query:
INSERT INTO <table>(NAME,VERSION) VALUES (<name>,result+1)
This approach is very straight-forward, easy-to-read and implement, however, not really performant because of so many queries necessary.
You can achieve that with sql alone with sql analytics and window functions, e.g.:
SELECT NAME, ROW_NUMBER() over (partition BY NAME ORDER BY NAME) as VERSION FROM<table>
You can then save the result of this query as a table using CREATE TABLE as SELECT...
(The assumption here is that the first version is 1, if it is not the case, then one could slightly rework the query). This solution would be very performant even for large datasets.
You should get the name before insertion. In your case, if something went wrong then how would you know about it so you get the name before insert query.
Not sure but you try this:
declare int version;
if exists(SELECT name from table where name=?)
then
version = SELECT MAX(version) FROM table WHERE name = ?
version += 1
else
version = 1
end
Regards.
This is actually a bad plan, you might be changing what the user's specified data. That is likely to not be what is desired, maybe they're not trying to create a new version but just unaware that the one wanted already exists. But, you can create a function, which your java calls, not only inserts the requested version or max+1 if the requested version already exists. Moreover it returns the actual values inserted.
-- create table
create table nv( name text
, version integer
, constraint nv_uk unique (name, version)
);
-- function to create version or 1+max if requested exists
create or replace function new_version
( name_in text
, version_in integer
)
returns record
language plpgsql strict
as $$
declare
violated_constraint text;
return_name_version record;
begin
insert into nv(name,version)
values (name_in,version_in)
returning (name, version) into return_name_version;
return return_name_version;
exception
when unique_violation
then
GET STACKED DIAGNOSTICS violated_constraint = CONSTRAINT_NAME;
if violated_constraint like '%nv\_uk%'
then
insert into nv(name,version)
select name_in, 1+max(version)
from nv
where name = name_in
group by name_in
returning (name, version) into return_name_version;
return return_name_version;
end if;
end;
$$;
-- create some data
insert into nv(name,version)
select 'n1', gn
from generate_series( 1,3) gn ;
-- test insert existing
select new_version('n2',1);
select new_version('n1',1);
select *
from nv
order by name, version;

Can we use to_char( ) with GROUP BY in HQL

while executing the following query using Hibernate
select to_char(vdadCloseDate,'yyyymm'), count(*) from RmDashboardAccountDataBe where 1=1 and vdadRmId in('MK13','MK11') GROUP BY TO_CHAR(vdadCloseDate,'YYYYMM')
I'm getting the following exception,
java.sql.SQLSyntaxErrorException: ORA-00979: not a GROUP BY expression
Is there any way to handle this issue?
This is "pure" Oracle SQL (i.e. not HQL) which looks exactly like your query (I had to use different table and column names, though):
SQL> select to_char(hire_date, 'yyyymm'), count(*)
2 from employees
3 where department_id in (10, 20)
4 group by to_char(hire_date, 'yyyymm');
TO_CHA COUNT(*)
------ ----------
200309 1
200508 1
200402 1
SQL>
So - yes, it works OK.
This is a link to HQL Group by clause which also suggests that such a query is perfectly valid (have a look so that I wouldn't have to copy/paste its contents over here).
That's why I asked whether you're sure that this is the query that returned ORA-00979 error. As you responded that it is, huh, I wouldn't know what to say ...

How to order by a specific column a MySQL query with Java and Hibernate?

I'm trying to use the same query to sort my table columns but when I pass as a parameter the column to ORDER BY, it adds quotes before and after my column name. If you are using ORDER BY parameter, the column name have to be written without being between quotes or MySQL is going to ignore it.
Example or query to execute:
select * from app_user ORDER BY mobile_token ASC LIMIT 0 , 20
This is what hibernate send to MySQL:
select * from app_user ORDER BY 'mobile_token' ASC LIMIT 0 , 20
Java query:
query = JPA.em().createNativeQuery("select * from app_user ORDER BY :column ASC LIMIT :init , :page",AppUser.class);
query.setParameter("column", column);
query.setParameter("init", pageNumber*pageSize);
query.setParameter("page", pageSize);
I could change the NativeQuery by:
"select * from app_user ORDER BY "+column+" ASC LIMIT :init , :page"
but this is going to become my app unsafety.
You can only pass values as parameters to a query. Not column or field names. That would make it impossible for the database to know which columns are actually used in the query, and thus make it impossible to prepare the execution plan.
So your solution using concatenation is the only one. Just make sure the column doesn't come from the user. Or if it comes from the user, that it's a valid column name and that the user is allowed to use it.

How to display values from sql statement

I am using netbeans.
I have a table 'incident' with column 'priority' which can hold values priority 1 , priority 2.
I have created a jcombobox with 3 options - select all( to select all the rows) / priority 1(to select rows with priority 1 and so on) / priority 2. Options are passed through prioritybox.getSelected().
I want to know any possible sql statement so that if i select the option 'select all' , all the entries of the table should be selected.
If i choose priority 1
then the statement
select * from incident where priority='"+prioritybox.getSelected()+"';
gets executed correctly i.e. it select the rows which having priority=priority1. But if i select the option 'select all ' then this statement becomes invalid as no such row is there with priority value = select all.I don't want to use if-else . Any other possible solution??
You need to use if clause.
Go with the following code
String strSelect1 = "select * from table";
if(prioritybox.getSelectedItem()!="your option")
{ strSelect1=strSelect1+" where Priority='"+prioritybox.getSelectedItem()+"'"; };
Now keep on adding this if statements for rest of the fields.
For purely SQL you could do:
select * from incident where priority='"+prioritybox.getSelected()+"' OR '"+prioritybox.getSelected()+"' = 'select all';
However it's probably easier to add logic to add the where clause if the option is not select all.
You can pass special character '%' to query which will return all characters for that you need to replace = with like query and you need to set '%' as your 'SelectAll' value, your query can be like this
select * from incident where priority like '"+prioritybox.getSelected()+"';
String query="";
if(!prioritybox.getSelected().toString().equals("select all")){
query="select * from incident where priority='"+prioritybox.getSelected()+"'";
}
else {
query="select * from incident" ;
}

Select query with multiple where clauses

I have a list of serial numbers: 111111, 222222, AAAAAA, FFFFFF and I want to return a corresponding value or null from a table depending on whether or not the value exists.
Currently I loop through my list of serial numbers, query using the following statement:
"SELECT cnum FROM table WHERE serial_num = " + serialNumber[i];
and then use the value if one is returned.
I would prefer to do this is one query and get results similar to:
Row | cnum
------------
1 | 157
2 | 4F2
3 | null
4 | 93O
5 | null
6 | 9F3
Is there a query to do this or am I stuck with a loop?
It sounds as if you have some sort of Java Array or Collection of serial numbers, and perhaps you want to check to see if these numbers are found in the DB2 table, and you'd like to do the whole list all at once, rather than one at a time. Good thinking.
So you want to have a set of rows with which you can do a left join to the table, with null indicating that the corresponding serial was not in the table. Several answers have started to use this approach. But they are not returning your row number, and they are using SELECT UNION's which seems a round-about way to get what you want.
VALUES clause
Your FROM clause can be a "nested-table-expression"
which can be a (fullselect)
with a correlation-clause. The (fullselect) can, in turn, be a VALUES clause. So you could have something like this:
FROM (VALUES (1, '157'), (2, '4F2'), (3, '5MISSING'), (4, '93O'), ...
) as Lst (rw, sn)
You can then LEFT JOIN this to the table, and get a two-column result table like you asked for:
SELECT Lst.rn, t.serial_num
FROM (VALUES (1, '157'), (2, '4F2'), (3, '5MISSING'), (4, '93O'), ...
) as Lst (rw, sn)
LEFT JOIN sometable t ON t.serial_num = Lst.sn
With this method, you will probably need a loop to build your dynamic SQL statement string, using the values from your collection.
If it was embedded SQL, we might be able to reference a host array variable containing your serial numbers. But alas, in Java I am not sure how to manage using the list directly in SQL, without using some loop.
If you use only an "in" it is not going to return null for the missing value forcing you to do some coding in the application (probably the most efficient way).
If you wanted the database to do all the work (may or may not be ideal) then
you would have to trick db2 into returning your list regardless.
Something like this might work, faking the null values to be returned from sysdummy with the common table expression (with part):
with all_serials as (
select '111111' as serialNumber from sysibm.sysdummy1 union all ,
select '222222' as serialNumber from sysibm.sysdummy1 union all ,
select 'AAAAAA' as serialNumber from sysibm.sysdummy1 union all ,
select 'FFFFFF' as serialNumber from sysibm.sysdummy1
)
select
t1.serialNumber,
t2.serialNumber as serialNumberExists
from
all_serials as t1 left outer join
/* Make sure the grain of the_Table is at "serialNumber" */
the_table as t2 on t1.serialNumber = t2.serialNumber
You can use the SQL IN keyword. You'd need to dynamically generate the list, but basically it'd look like:
SELECT cnum FROM table WHERE serial_num in ('111111', '2222222', '3333333', 'AAAAAAA'...)
Try something like:
select t.cnum
from
(select '111111' serial_num from sysibm.sysdummy1 union all
select '222222' serial_num from sysibm.sysdummy1 union all
select 'AAAAAA' serial_num from sysibm.sysdummy1 union all
select 'FFFFFF' serial_num from sysibm.sysdummy1) v
left join table t on v.serial_num = t.serial_num
I'm not sure if I get you correctly, but this could help:
String query = "SELECT cnum FROM table WHERE ";
for(int i = 0; i < serialNumber.length; i++)
query += "serial_num='" + serialNumber[i] + "' OR ";
query += "serial_num IS NULL "
System.out.println(query);

Categories