I am writing one application to search for specific fields in the form and display the corresponding query to the user (user can save the executed query and load the query later). I am using an API to generate the query based on the selected fields. The API returns the generated query in following format.
Generated query:
SELECT T0."id" AS "COL0"
FROM student_table T0
WHERE ( ( ( T0."student_name" = ? )
AND ( T0."grade" = ? ) )
AND ( T0."student_no" LIKE ? ) )
Expected query:
SELECT T0."id" AS "COL0"
FROM student_table T0
WHERE ( ( ( T0."student_name" = 'John' )
AND ( T0."grade" = 'A' ) )
AND ( T0."student_no" LIKE '12%' ) )
Now I have to build proper query from above query. Since I have the field values in the from, I want to replace the "?" with corresponding values.
Is it possible to replace the '?' with corresponding field values using java string replaceAll method ? can we use the concept of back references to replace the question marks with corresponding values ? If not possible with regex please suggest a better approach to solve the problem.
Edit
I think prepared statement wont help much since I need to automate this process for hundreds of fields which are unordered and it is a very costly operation also. (correct me If I am wrong)
I am actually using my company specific platform API, I am adding criteria's and query columns to the query definition and it returns the executed query in the above mentioned format. User should be able to save the Expected query, I should display both the query and result of query in UI.
Try to execute query in following way
String insertTableSQL = "SELECT T0.\"ID\" AS \"COL0\" FROM STUDENT_TABLE T0 WHERE ( ( ( T0.\"STUDENT_NAME\" = ? ) AND ( T0.\"GRADE\" = ? ) ) AND ( T0.\"STUDENT_NO\" LIKE ? ) )";
PreparedStatement preparedStatement = dbConnection.prepareStatement(insertTableSQL);
preparedStatement.setString(1, "John");
preparedStatement.setString(2, "A");
preparedStatement.setString(3, "12%");
preparedStatement.execute();
You can use PreparedStatement to execute such queries that contains ?
PreparedStatement ps = null;
ResultSet rs = null;
String sQuery = "SELECT T0."ID" AS "COL0" FROM STUDENT_TABLE T0 WHERE ( ( ( T0."STUDENT_NAME" = ? ) AND ( T0."GRADE" = ? ) ) AND ( T0."STUDENT_NO" LIKE ? ) )";
ps = objConnection.prepareStatement(sQuery);
ps.setString(1, valueOf_T0."STUDENT_NAME");
ps.setString(2, valueOf_T0."GRADE");
ps.setString(3, valueOf_T0."STUDENT_NO");
rs = ps.executeQuery();
As you have data values and you know the type of database fields, so that you can set values in setter methods according to your data type like setInt(), setDouble(),etc.
Read more about : http://docs.oracle.com/javase/6/docs/api/java/sql/PreparedStatement.html
Thanks
Related
I have the below SP which I am trying to convert into simple Java inline query :
CREATE OR REPLACE PROCEDURE public.spdummytable(
par_zone_no integer,
par_fpsallocid integer,
INOUT p_refcur refcursor)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
OPEN p_refcur FOR
SELECT
z.zone_no,
m AS monthnumber,
COALESCE(fpsallocid, par_FPSallocid) AS fpsallocid,
to_char((CAST ('2000-01-01' AS TIMESTAMP WITHOUT TIME ZONE))::TIMESTAMP + (COALESCE(aw.month, m) - 1::NUMERIC || ' MONTH')::INTERVAL, 'Month') AS monthname,
week1,
week2,
week3,
week4
FROM (SELECT par_Zone_No AS zone_no) AS z
CROSS JOIN (SELECT 1 AS m
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 10
UNION SELECT 11
UNION SELECT 12) AS moty
LEFT OUTER JOIN anotherTable AS aw
ON z.zone_no = aw.zone_no AND
aw.month = moty.m AND
COALESCE(fpsallocid, par_FPSallocid) = par_FPSallocid;
END;
$BODY$;
ALTER PROCEDURE public.spdummytable(integer, integer, refcursor)
OWNER TO postgres;
This will fetch some weekly values for every month from Jan to Dec.
What I am trying is below :
public List<MyResponse> result = null;
Connection conn = DatabaseConnection.connect();
PreparedStatement stmt = conn.prepareStatement("call public.spdummytable(?,?,?,,....,cast('p_refcur' as refcursor)); FETCH ALL IN \"p_refcur\";");
stmt.setString(1, "8006");
stmt.setString(2, "8049");
----
----
boolean isResultSet = stmt.execute();
if(isResultSet) {
ResultSet rs = stmt.getResultSet();
while(rs.next()) {
MyResponse myResponse = new MyResponse();
myResponse.setSomeVariable(rs.getString("columnname"));
-----
-----
result.add(myResponse)
}
rs.close();
conn.close();
}
But I am confused on the query formation part from the above SP. This seems to be a complex conversion. Can someone please help form the inline query. Appreciate your help on this.
EDIT/UPDATE
If I am unable to explain myself, I just want to say that I need to form the postgresql SELECT query from the above SP. I know the PreparedStatement is wrong above, I am trying to form a basic sql query from the above SP . Changing/Modifying the SP is not an option for me. I am planning to cut the dependency from the database and control it over Java. Please help.
I don't think getResultSet works with a stored procedure like that but I'm unsure. You're operating on a cursor with your INOUT parameter. As suggested in the comments, this would be much easier with a set returning function.
Note: stored procedures didn't exist in Postgres before Postgres 11.
If you cannot convert this to a set returning function, you'll need to handle the cursor object in a different manner. Something like this:
CallableStatement stmt = conn.prepareCall("{? = call public.spdummytable(?,?) }");
stmt.registerOutParameter(1, Types.OTHER);
stmt.setString(2, "8006");
stmt.setString(3, "8049");
stmt.execute();
ResultSet results = (ResultSet) stmt.getObject(1);
while (results.next()) {
// do something with the results.
}
Set returning function:
CREATE OR REPLACE FUNCTION public.spdummytable
( par_zone_no INTEGER
, par_fpsallocid INTEGER
)
RETURNS TABLE ( zone_no INTEGER -- I don't know the data types of these fields
, monthnumber INTEGER
, fpsallocid INTEGER
, monthname TEXT
, week1 TEXT
, week2 TEXT
, week3 TEXT
, week4 TEXT
)
AS $$
BEGIN
RETURN QUERY
SELECT z.zone_no AS zn
, moty AS mo_num
, COALESCE(fpsallocid, par_FPSallocid) AS fpsid
, to_char((CAST ('2000-01-01' AS TIMESTAMP WITHOUT TIME ZONE))::TIMESTAMP + (COALESCE(aw.month, m) - 1::NUMERIC || ' MONTH')::INTERVAL, 'Month') AS mo_name
, week1 w1
, week2 w2
, week3 w3
, week4 w4
FROM (SELECT par_Zone_No AS zone_no) AS z
CROSS JOIN generate_series(1, 12) AS moty
LEFT OUTER JOIN anotherTable AS aw ON z.zone_no = aw.zone_no
AND aw.month = moty
AND COALESCE(fpsallocid, par_FPSallocid) = par_FPSallocid
;
END;
$$ LANGUAGE PLPGSQL;
You could also define your own return type and use RETURNS SETOF your_type. Use the same function body as above.
CREATE TYPE type_dummytype AS
( zone_no INTEGER -- I don't know the data types of these fields
, monthnumber INTEGER
, fpsallocid INTEGER
, monthname TEXT
, week1 TEXT
, week2 TEXT
, week3 TEXT
, week4 TEXT
);
CREATE OR REPLACE FUNCTION public.spdummytable
( par_zone_no INTEGER
, par_fpsallocid INTEGER
)
RETURNS SETOF type_dummytype
AS $$ ... $$
Then your prepared statement becomes something like this:
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM public.spdummytable(?, ?);");
stmt.setString(1, "8006");
stmt.setString(2, "8049");
All of the other java should be good to go.
I have some data in an SQLite Database. The following queries in SQLiteBrowser give me exactly the ResultSet I want:
With TempTable0 AS (SELECT Source, Date, Value AS Close FROM FinData WHERE Type = 'Close'),
TempTable1 AS (SELECT Source, Date, Value AS Colume FROM FinData WHERE Type = ' Colume')
SELECT DISTINCT TempTable0.Date, Close, Colume FROM TempTable0
JOIN TempTable1 ON TempTable1.Date = TempTable0.Date
Now, I tried to get this working as a single query via a simple executeQuery-Method from Java and read the data in a Matrixlike-DataStructure:
String sql = "With TempTable0 AS (SELECT Source, Date, Value AS Close FROM FinData WHERE Type = 'Close'), TempTable1 AS (SELECT Source, Date, Value AS Colume FROM FinData WHERE Type = ' Colume') SELECT DISTINCT TempTable0.Date, Close, Colume FROM TempTable0 JOIN TempTable1 ON TempTable1.Date = TempTable0.Date";
Connection Conn = DriverManager.getConnection(SQLCommunication.urlDB);
Statement stmt = Conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next() ) {
String res = rs.getString("Close");
DataMatrix.get("Close").add(res);
res = rs.getString("Colume");
DataMatrix.get("Colume").add(res);
}
However, the Resultset returns null.
I suspect this is because, it cannot work with two dependent SQLite-Queries, however, I have no idea, how to solve this.
I am no expert on SQLite and Java interaction and I am really running out of ideas, right now. Do you have any sugestions? (Even maybe a tip on nesting these two statements in one so it can be executed in one shot?)
Thanks so much!
Wiwi
In my code I have an oracle connection on which I execute the following:
stmt.executeUpdate("ALTER SESSION SET NLS_TERRITORY='GREECE'");
Now, if I select a number from my table - let's say its value is 0,25 with
rs.getString(1) what I get is '0.25' not '0,25' as I was expecting...
What am I missing here?
Thanks.
Edit 1.
the sql below:
select
value
from nls_session_parameters
where parameter = 'NLS_NUMERIC_CHARACTERS'
inside my session returns ',.'
which means that for the oracle comma is used as decimal separator.
It is also confirmed by the fact that when inserting into table - comma is expected - otherwise exception is thrown.
So the question remains... why getString behaves this way?
The value in your query is a NUMBER and it is being passed to Java as a NUMBER and stored in the ResultSet as a NUMBER then you are using rs.getString() to get the number as a String.
Oracle has nothing to do with the conversion of the data types, it is all internal to Java.
st = con.prepareStatement( "ALTER SESSION SET NLS_TERRITORY='GREECE'" );
st.executeUpdate();
st.close();
st = con.prepareStatement( "SELECT 1.23, TO_CHAR( 1.23 ) FROM DUAL" );
ResultSet rs = st.executeQuery();
ResultSetMetaData md = rs.getMetaData();
System.out.println( md.getColumnTypeName( 1 ) + " " + md.getColumnTypeName( 2 ) );
while( rs.next() )
{
System.out.println( rs.getString( 1 ) + " " + rs.getString( 2 ) );
}
Outputs:
NUMBER VARCHAR2
1.23 1,23
If you want Oracle to format the number then use TO_CHAR to format the number so that is passed to Java as a String and not a number.
This maybe help you;
select value
from nls_session_parameters
where parameter = 'NLS_NUMERIC_CHARACTERS';
VALUE
----------------------------------------
.,
Check this value. If you want to with ',' (0,25) change parameter like ;
alter session set NLS_NUMERIC_CHARACTERS = ',.';
Change ',' and '.' order.
I'm required to write a couple of UPDATE queries to parse data from a .csv file, but i do not know how the table works, i don't even have direct access to the Database, i just got an INSERT query like this one :
insert into lr_umbrales_valores (umcod_id, uvfec_dt, uvval_nm)
values ((select umcod_id from lr_umbrales
where lrcod_nm = (
select lrcod_id from lr_lineas_referencia
where me_metrica_nm = ?
and fecha_baja_dt is null)
and umtip_tx='S'), sysdate, ?)
So i'm trying this :
UPDATE LR_UMBRALES_VALORES SET UVVAL_NM = ?
WHERE (
SELECT UMCOD_ID FROM LR_UMBRALES
WHERE LRCOD_NM = (
SELECT LRCOD_ID FROM LR_LINEAS_REFERENCIA
WHERE ME_METRICA_NM = ?
AND FECHA_BAJA_DT IS NULL
)
AND UMTIP_TX = 'S')
AND UVFEC_DT = TO_DATE(?, 'DD/MM/YYYY HH24:MI:SS')");
This gives me a 'Missing Expression' Error (ORA-00936)
This is the only Information about the tables i got :
This is the Table i need to Update (its UVVAL_NM)
From this one, i get the UMCOD_ID when LRCOD_NM is the same as the LRCOD_ID from the next table.
Get the LRCOD_ID when ME_METRICA_NM is the same as the '?' parameter
Any tip in how to approach this? Needless to say, i'm completely new with SQL so this may be a very obvious mistake but i cannot get it right.
This part
WHERE (
SELECT UMCOD_ID FROM LR_UMBRALES
WHERE LRCOD_NM = (
SELECT LRCOD_ID FROM LR_LINEAS_REFERENCIA
WHERE ME_METRICA_NM = ?
AND FECHA_BAJA_DT IS NULL
)
AND UMTIP_TX = 'S')
causes the error. Maybe you wanted something like
WHERE UMCOD_ID = (
SELECT UMCOD_ID FROM LR_UMBRALES
WHERE LRCOD_NM = (
SELECT LRCOD_ID FROM LR_LINEAS_REFERENCIA
WHERE ME_METRICA_NM = ?
AND FECHA_BAJA_DT IS NULL
)
AND UMTIP_TX = 'S')
Suppose I have the query:
SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID = ?
With PreparedStatement, I can bind the variable:
pstmt.setString(1, custID);
However, I cannot obtain the correct results with the following binding:
pstmt.setString(1, null);
As this results in:
SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID = NULL
which does not give any result. The correct query should be:
SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID IS NULL
The usual solutions are:
Solution 1
Dynamically generate query:
String sql = "SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID "
+ (custID==null ? "IS NULL" : "= ?");
if (custID!=null)
pstmt.setString(1, custID);
Solution 2
Use NVL to convert null value to a gibberish value:
SELECT * FROM CUSTOMERS WHERE NVL(CUSTOMER_ID, 'GIBBERISH') = NVL(?, 'GIBBERISH');
But you need to be 100% sure that the value 'GIBBERISH' will never be stored.
Question
Is there a way to use a static query and avoid depending on gibberish value conversions? I am looking for something like:
SELECT * FROM CUSTOMERS
WHERE /** IF ? IS NULL THEN CUSTOMER_ID IS NULL ELSE CUSTOMER_ID = ? **/
I think I may have a working solution:
SELECT * FROM CUSTOMERS
WHERE ((? IS NULL AND CUSTOMER_ID IS NULL) OR CUSTOMER_ID = ?)
pstmt.setString(1, custID);
pstmt.setString(2, custID);
Will the above work reliably? Is there a better way (possibly one that requires setting the parameter only once)? Or is there no way to do this reliably at all?
Your working solution is fine (and similar to what I've used before). If you only want to bind once you can use a CTE or inline view to provide the value to the real query:
WITH CTE AS (
SELECT ? AS REAL_VALUE FROM DUAL
)
SELECT C.* -- but not * really, list all the columns
FROM CTE
JOIN CUSTOMERS C
ON (CTE.REAL_VALUE IS NULL AND C.CUSTOMER_ID IS NULL)
OR C.CUSTOMER_ID = CTE.REAL_VALUE
So there is only one placeholder to bind.
I don't really see a problem with a branch on the Java side though, unless your actual query is much more complicated and would lead to significant duplication.
WHERE
DECODE(CUSTOMER_ID, NULL, 'NULL', CUSTOMER_ID || 'NOT NULL') =
DECODE(?, NULL, 'NULL', CUSTOMER_ID, CUSTOMER_ID || 'NOT NULL')
This works, I believe
SQLFiddle
Note that in order to test it on sqlfiddle I have had to replace the parameter with a value for each case [NULL, 'NULL', 'SMITH']
Dynamically creating the query works on all JDBCs, so you are not bound to platform-specific SQL.
It wouldn't be that hard to read to create an if-branch, would it?
PreparedStatement pst = null; // Avoid initialisation warnings
if (custID == null)
pst = connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID IS NULL");
else {
pst = connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID = ?");
pst.setString(1, custID);
}
ResultSet rs = pst.executeQuery();