Get data from PostgreSQL function to java - java

I have written a simple function in PostgreSQL database. From my JAVA source code I am calling this function like
SELECT getData('active');
I am getting the data correct but the table header of the dataset is showing my function name (getdata) not userid and username. In this situation how I can get data?
CREATE or REPLACE FUNCTION getData(value text)
RETURNS TABLE(
userid integer,
username varchar(50)
) AS -- text AS --
$body$
DECLARE
fullsql TEXT;
records RECORD;
exeQuery TEXT;
BEGIN
fullsql:= 'SELECT userid, username from user_index where status='''value'''';
exeQuery := 'SELECT * FROM (' || fullsql || ') AS records';
RETURN QUERY EXECUTE exeQuery;
END
$body$
LANGUAGE plpgsql;
Currently the output is
getdate
--------------
501,alexanda
502,cathie
But I want to output like:
userid username
------|---------
501,alexanda
502,cathie
i am trying to acheive following:
SELECT usr.username FROM cust_invoice_index as inv
INNER JOIN
(SELECT getdata('active')) as usr ON (usr.userid=inv.userid_edit)
Following query is working fine:
SELECT usr.username FROM cust_invoice_index as inv
INNER JOIN
(SELECT userid, username from user_index where status='active') as usr ON (usr.userid=inv.userid_edit)

As your function returns a result set you should be using select * from getdata('active').
Don't put calls to set returning functions into the select list.
SELECT usr.username
FROM cust_invoice_index as inv
JOIN (SELECT * FROM getdata('active')) as usr ON usr.userid=inv.userid_edit

Related

Java dynamically generate SQL query - ATHENA

I am trying to generate sql query based on user input. There are 4 search fields on the UI:
FIRST_NAME, LAST_NAME, SUBJECT, MARKS
Based on user input I am planning to generate SQL query. Input can be of any combination.
eg: select * from TABLE where FIRST_NAME="some_value";
This query needs to be generated when FIRST_NAME is given and other fields are null
select * from TABLE where FIRST_NAME="some_value" and LAST_NAME="some_value";
This query needs to be generated when FIRST_NAME and LAST_NAME are given and other fields are null
Since there are 4 input fields, number of possible queries that can be generated are 24 (factorial of 4).
One idea is to write if condition for all 24 cases.
Java pseudo code:
String QUERY = "select * from TABLE where ";
if (FIRST_NAME!=null) {
QUERY = QUERY + "FIRST_NAME='use_input_value';"
}
if (LAST_NAME!=null) {
QUERY = QUERY + "LAST_NAME='use_input_value';"
}
if (SUBJECT!=null) {
QUERY = QUERY + "SUBJECT='use_input_value';"
}
if (MARKS!=null) {
QUERY = QUERY + "MARKS='use_input_value';"
}
I am not able to figure out how to generate SQL queries with AND coditions for multiple Input values.
I have been through concepts on dynamically generate sql query but couldn't process further.
Can someone help me on this.
FYI: I have been through How to dynamically generate SQL query based on user's selections?, still not able to generate query string based on user input.
Let's think about what would happen if you just ran the code you wrote and both FIRST_NAME and LAST_NAME are provided. You'll wind up with this:
select * from TABLE where FIRST_NAME='use_input_value';LAST_NAME='use_input_value';
There are two problems here:
The query is syntactically incorrect.
It contains the literals 'use_input_value' instead of the values you want.
To fix the first problem, let's first add and to the start of each expression, and remove the semicolons, something like this:
String QUERY = "select * from TABLE where";
if (FIRST_NAME!=null) {
QUERY = QUERY + " and FIRST_NAME='use_input_value'";
}
Notice the space before the and. We can also remove the space after where.
Now the query with both FIRST_NAME and LAST_NAME will look like this:
select * from TABLE where and FIRST_NAME='use_input_value' and LAST_NAME='use_input_value'
Better but now there's an extra and. We can fix that by adding a dummy always-true condition at the start of the query:
String QUERY = "select * from TABLE where 1=1";
Then we append a semicolon after all the conditions have been evaluated, and we have a valid query:
select * from TABLE where 1=1 and FIRST_NAME='use_input_value' and LAST_NAME='use_input_value';
(It may not be necessary to append the semicolon. Most databases don't require semicolons at the end of a single query like this.)
On to the string literals. You should add a placeholder instead, and simultaneously add the value you want to use to a List.
String QUERY = "select * from TABLE where";
List<String> args = new ArrayList<>();
if (FIRST_NAME!=null) {
QUERY = QUERY + " and FIRST_NAME=?";
args.add(FIRST_NAME);
}
After you've handled all the conditions you'll have a string with N '?' placeholders and a List with N values. At that point just prepare a query from the SQL string and add the placeholders.
PreparedStatement statement = conn.prepareStatement(QUERY);
for (int i = 0; i < args.size(); i++) {
statement.setString(i + 1, args[i]);
}
For some reason columns and parameters are indexed starting at 1 in the JDBC API, so we have to add 1 to i to produce the parameter index.
Then execute the PreparedStatement.

Optimizing SQL query with cte and variable inputs

I am trying to run the following SQL script using Java and am getting issues with no resultset from JDBCTemplate. I thought about reducing it using functions/stored procedures and would like some help with it:
SQL - first part:
SET NOCOUNT ON
IF OBJECT_ID('tempdb.dbo.#tempSearch', 'U') IS NOT NULL
DROP TABLE #tempSearch;
CREATE TABLE #tempSearch
(
ID INT,
Value VARCHAR(255)
)
INSERT INTO #tempSearch
VALUES (1, 'Variable1'), (2, 'Variabl2');
Second part:
WITH cte AS
(
SELECT
RoleID,
',' + REPLACE(REPLACE(GroupNames, ',', ',,'), ' ', '') + ',' GroupNames
FROM
UserGroup_Role_Mapping
), cte2 AS
(
SELECT
cte.RoleID,
REPLACE(cte.GroupNames, ',' + Value + ',', '') AS GroupNames,
s.ID, s.Value
FROM
cte
JOIN
#tempSearch s ON ID = 1
UNION ALL
SELECT
cte2.RoleID,
REPLACE(cte2.GroupNames, ',' + s.Value + ',', '') AS l,
s.ID, s.Value
FROM
cte2
JOIN
#tempSearch s ON s.ID = cte2.ID + 1
)
SELECT
a.Role, a.Sort_Order,
a.Parent, a.Parent_ID, a.Parent_URL,
a.Child, a.Child_ID,a.Child_URL
FROM
Config_View a
WHERE
a.Role IN (SELECT Name
FROM
(SELECT DISTINCT RoleID FROM cte2 WHERE LEN(GroupNames) = 0) tempRoles
JOIN
User_Role ON tempRoles.RoleID = User_Role.ID
)
DROP TABLE #tempSearch
I was thinking first part can be done in a stored procedure. I did read here (stored procedure with variable number of parameters) about making a table from a list of variables but am not sure how to do set those variables in a loop like i am doing from above (1,Variable1 etc.).
I think the second part can be by itself?
So my updated query might be:
Call stored procedure (variable1, ..., variablex);
SQL part 2?
If anyone can help that would be great!
It's possible to do this in two seperate batches, but only if you can ensure that the first batch runs in session scope, and not in a nested batch ( eg via sp_executesql ). Temp tables created in nested batches, like stored procedures or prepared statements are automatically destroyed at the end of the nested batch. So it depends on how you call it. My guess is that a PreparedStatement won't work.
The right way to do this is probably to use a stored procedure with a table-valued parameter, or a JSON (for SQL 2016+), or XML parameter and parses it in the stored procedure body. See https://learn.microsoft.com/en-us/sql/connect/jdbc/using-table-valued-parameters?view=sql-server-2017
You can also use a TSQL batch instead of a stored procedure and bind a Table-Valued Parameter, or a NVarchar(max) parameter containing JSON.
With a TVP you could simply use a batch like:
with s as (
select * from ? --bind a table-valued parameter here
), cte as (
select RoleID,','+replace(replace(GroupNames,',',',,'),' ','')+',' GroupNames from UserGroup_Role_Mapping
)
,cte2 as(
select cte.RoleID, replace(cte.GroupNames,','+Value+',','') as GroupNames, s.ID, s.Value
from cte
join s on ID=1
union all
select cte2.RoleID, replace(cte2.GroupNames,','+s.Value+',','') as l, s.ID ,s.Value
from cte2
join s on s.ID=cte2.ID+1
)
SELECT a.Role, a.Sort_Order, a.Parent, a.Parent_ID, a.Parent_URL, a.Child, a.Child_ID,a.Child_URL
FROM Config_View a
WHERE a.Role IN (
Select Name from (
Select distinct RoleID from cte2 where len(GroupNames)=0
) tempRoles
join User_Role
on tempRoles.RoleID = User_Role.ID
)
That would be the value of the string variable sql, and then call it something like:
SQLServerPreparedStatement pStmt = (SQLServerPreparedStatement) connection.prepareStatement(sql);
pStmt.setStructured(1, "dbo.CategoryTableType", sourceTVPObject);
ResultSet rs = stmt.executeQuery();

List of columns in sql query

I have a query using various joins, and I just need the list of columns which are returned by this query. I done it in java, by asking only one row with rownum=1 and getting column name for value.The problem is if there is no data returned by that query.
For ex.
select * from something
and if there is any data returning by this query then it will return col1,col2,col3.
But if there is no data returned by this query, then it will throw error.
What I need is
Is there any way that I can run
desc (select * from something)
or similar to get list of columns returned by query.
It can be in sql or JAVA. Both methods are acceptable.
In my application, user passes the query, and I can add wrapper to the query but I cant modify it totally.
The flow of application is
query from user -> execute by java and get one row -> return list of columns in the result set.
you can use ResultSetMetaData of resultset
for example :
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM TABLE2");
ResultSetMetaData rsmd = rs.getMetaData();
int countOfColumns = rsmd.getColumnCount();
for(int i = 1; i <= countOfColumns ; i++ )
System.out.println(rsmd.getColumnName(i));
you could maybe convert your query to a view, you can then see the columns in the view by querying user_tab_columns
select * from user_tab_columns
The Oracle equivalent for information_schema.COLUMNS is USER_TAB_COLS for tables owned by the current user, ALL_TAB_COLS or DBA_TAB_COLS for tables owned by all users.
Tablespace is not equivalent to a schema, neither do you have to provide the tablespace name.
Providing the schema/username would be of use if you want to query ALL_TAB_COLS or DBA_TAB_COLS for columns OF tables owned by a specific user. in your case, I'd imagine the query would look something like:
String sqlStr= "
SELECT column_name
FROM all_tab_cols
WHERE table_name = 'users'
AND owner = ' || +_db+ || '
AND column_name NOT IN ( 'password', 'version', 'id' )
"
Note that with this approach, you risk SQL injection.

Calling a PostgreSQL function from Java

I have written a function that I would like to call in Java. But I don't think it is able to do anything with the query that I passed. Following is my code from java:
String QUERY_LOCATION = "select (license_plate) as test from carInst( (select category_name from reservation where rid = ?) , (select lname from reservation where rid = ?))";
//PreparedStatement check_location = null;
PreparedStatement check_location = connection.prepareStatement(QUERY_LOCATION);
check_location.setInt(1, rid);
check_location.setInt(2, rid);
rs = check_location.executeQuery();
if (rs.next()) {
System.out.print("Car found: "+rs.getString("test")+"\n");
license_plate = rs.getString("test");
update_reservation.setString(5, license_plate);
bool = false;
} else {
System.out
.print("There is no car available\n");
}
And following is my stored procedure written in PL/pgSQL (PostgreSQL):
CREATE OR REPLACE FUNCTION carInst(cname varchar(20), loc varchar(20) )
RETURNS TABLE (license_plate varchar(6) ) AS $$
BEGIN
DECLARE cur CURSOR
FOR SELECT carinstance.license_plate, carmodel.category_name, carinstance.lname FROM carinstance,carmodel
WHERE carinstance.mid = carmodel.mid ;
BEGIN
FOR rec IN cur LOOP
RETURN QUERY SELECT distinct carinstance.license_plate FROM Carinstance
WHERE rec.category_name = cname
AND rec.lname = loc
AND rec.license_plate=carinstance.license_plate;
END LOOP;
END;
END;
$$ LANGUAGE plpgsql;
When I run the code in Java, the print statement prints a null value for Car found. I would really appreciate some help here.
Problems
Most importantly, the query in the LOOP is nonsense. You select rows from carinstance, but all conditions are on rec. This select all rows multiple times.
One END too many. FOR has no END, only LOOP has.
Whenever you feel the temptation to work with an explicit cursor in plpgsql, stop right there. Chances are, you are doing it wrong. A FOR loop has an implicit cursor anyway.
Don't mess with mixed case identifiers without double quotes. I converted all identifiers to lower case.
You use one simple query, spread out over a cursor and another query. This can all be much simpler.
Solution
Try this simple SQL function instead:
CREATE OR REPLACE FUNCTION car_inst(_cname text, _loc text)
RETURNS TABLE (license_plate text)
LANGUAGE sql AS
$func$
SELECT DISTINCT ci.license_plate
FROM carmodel cm
JOIN carinstance ci USING (mid)
WHERE cm.category_name = $1
AND ci.lname = $2
$func$;
Call:
SELECT license_plate AS test FROM car_inst(
(SELECT category_name FROM reservation WHERE rid = ?)
, (SELECT lname FROM reservation WHERE rid = ?)
);
Or build it all into your function:
CREATE OR REPLACE FUNCTION car_inst(_cname text, _loc text)
RETURNS TABLE (license_plate text)
LANGUAGE sql AS
$func$
SELECT DISTINCT ci.license_plate
FROM carmodel cm
JOIN carinstance ci USING (mid)
JOIN reservation r1 ON r1.category_name = cm.category_name
JOIN reservation r2 ON r2.lname = ci.lname
WHERE r1.rid = $1
AND r2.rid = $2;
$func$;
Call:
"SELECT license_plate AS test FROM car_inst(? , ?)";
Remember: The OUT parameter license_plate is visible anywhere in the body of the function. Therefore you must table-qualify the column of the same name at all times to prevent a naming collision.
DISTINCT may or may not be redundant.

Mysql - result the data's with or with out available in DB

This is my sql query to sum the error count in DB. What made the problem below is, when I check the available data in this, it retrieve the actual and correct data given, if I'm giving any wrong (that is unavailable data in table) input it gives nothing that is it doesn't throw any output screen:
SELECT
SUM(IF(ieb.errortype_id='1',1,0)) AS Total_Critical,
SUM(IF(ieb.errortype_id='2',1,0)) AS Total_Non_critical,
u.emp_id,
u.emp_name
FROM
issueerror_empmap iem,
issueerror_body ieb,
USER u,
issueerror_type iet
WHERE
ieb.chapter_id = '424262' AND
iem.chapter_errorid=ieb.chapter_errorid AND
iem.emp_id = u.emp_id AND
u.emp_id = '693' AND
ieb.errortype_id=iet.errortype_id
GROUP BY
iem.emp_id,
u.emp_name
ORDER BY
iem.emp_id
Can any one please suggest me how to build this query to retrieve the data if available and display the output as null if the input data not available in a table.
For producing the output for the above query is (if data available in a table):
Total_Critical Total_Non_Critical emp_id emp_name
4 10 xxx xxx
For producing the output for the above query is (if data not available in a table):
Total_Critical Total_Non_Critical emp_id emp_name
I am expecting the emp_id and emp_name with the error count as null or zeroes.
how about this query?
select
sum (Total_Critical) as Total_Critical,
sum(Total_Non_critical) as Total_Non_critical,
emp_id,
emp_name from
(
(SELECT
SUM(IF(ieb.errortype_id='1',1,0)) AS Total_Critical,
SUM(IF(ieb.errortype_id='2',1,0)) AS Total_Non_critical,
u.emp_id,
u.emp_name
FROM
issueerror_empmap iem,
issueerror_body ieb,
USER u,
issueerror_type iet
WHERE
ieb.chapter_id = '424262' AND
iem.chapter_errorid=ieb.chapter_errorid AND
iem.emp_id = u.emp_id AND
u.emp_id = '693' AND
ieb.errortype_id=iet.errortype_id
GROUP BY
iem.emp_id,
u.emp_name
ORDER BY
iem.emp_id )
) UNION ALL (
select 0,0,null, null
)
) as tbl1

Categories