Optimizing SQL query with cte and variable inputs - java

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();

Related

could not read column value from result set in Hibernate Native Query

SELECT
CONCAT(CREG.FIRSTNAME, ' ', CREG.LASTNAME) AS NAME,
CASE WHEN CR.CAMPAIGNTYPE = 'NPS'
THEN NPSSCORE
ELSE CSATSCORE END AS SCORE,
IFNULL(cast(CC.TEXT AS CHAR(255)), '') AS COMMENTS,
CREG.ID AS CLIENTID,
CR.ID AS CAMPAIGNRESPONSEID,
CI.ID AS ISSUEID
FROM CUSTOMER_ISSUES CI INNER JOIN (SELECT DISTINCT ISSUEID
FROM ISSUE_DEPARTMENT_MAPPING
WHERE CUSTOMERUSERID = 91 AND ISSUE_STATUS = 'New') IDM
ON CAST(CI.FEEDBACK_DATE AS DATE) BETWEEN '2016-06-05' AND '2016-06-11' AND IDM.ISSUEID = CI.ID
INNER JOIN CAMPAIGN_RESPONSE CR ON CR.ID = CI.CAMPAIGN_RESPONSE_ID
INNER JOIN CLIENT_REGISTRATION CREG ON CREG.ID = CR.RESPONSECUSTOMERID
LEFT OUTER JOIN CAMPAIGN_COMMENTS CC ON CC.CAMPAIGN_RESPONSE_ID = CR.ID;
The above query is running in the mysql-console properly ,but when I am integrating with the Hibernate,following error is thrown by Hibernate.
[BigIntegerType] could not read column value from result set: ID; Column 'ID' not found.
Try getting rid of the alias names in your SQL query.
Basically what happens here is when you run
SELECT
CONCAT(CREG.FIRSTNAME, ' ', CREG.LASTNAME) AS NAME,
CASE WHEN CR.CAMPAIGNTYPE = 'NPS'
THEN NPSSCORE
ELSE CSATSCORE END AS SCORE,
IFNULL(cast(CC.TEXT AS CHAR(255)), '') AS COMMENTS,
CREG.ID AS CLIENTID,
CR.ID AS CAMPAIGNRESPONSEID,
CI.ID AS ISSUEID
in JDBC it returns the column as CREG.ID instead of ClientID.
So try running the query without the aliases, typically, there is a problem in JDBC with this. If you still insist on using aliases,add the following entry to JDBC URL in configuration file
[useOldAliasMetadataBehavior=true]

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.

Get data from PostgreSQL function to 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

Getting an error while calling mySql proceedure

I have created a single procedure for update for different table say country & department.
and in procedure i'hve mentioned an input parameter for table name along with other parameter.
But unfortunately i got an error. Here is mySql Procedure:
CREATE DEFINER=`satish`#`%` PROCEDURE `p_update_Master_Name`(
IN tbl_Name VARCHAR(35),
IN tbl_column_old_value VARCHAR(35),
IN tbl_column_new_value VARCHAR(35),
IN tbl_user_id INT,
OUT msg INT
)
BEGIN
IF EXISTS (SELECT Name from tbl_name where Name = tbl_column_new_value) then
SET msg := '1';
-- failed case
else
UPDATE tbl_name SET Name= tbl_column_value, Modified_Date=now(), Modified_by=tbl_user_id where Name = tbl_column_old_value;
set msg := '0';
-- success
END IF;
END
Im calling this procedure from java file.
CallableStatement cs = conn.prepareCall("{ call p_update_Master_Name(?,?,?,?,?)}");
cs.setString(1, "country");
cs.setString(2, real);
cs.setString(3, mod);
cs.setInt(4, 01);
cs.execute();
cs.registerOutParameter(5, Types.INTEGER);
int i=cs.getInt(5);
but it gives me a mysql.jdbc exception.
com.mysql.jdbc.exceptions.MySQLSyntaxErrorException: Table 'sims.tbl_name' doesn't
exist
Please help me. Thanx in advance
You can't use variables to define table or column (or any other db object for that matter) names in static SQL queries. They should be literals.
You have to use dynamic SQL to achieve your goal. Read more on the topic SQL Syntax for Prepared Statements
Your stored procedure might look like
DELIMITER $$
CREATE PROCEDURE p_update_Master_Name
(
IN tbl_Name VARCHAR(35),
IN tbl_column_old_value VARCHAR(35),
IN tbl_column_new_value VARCHAR(35),
IN tbl_user_id INT,
OUT msg INT
)
BEGIN
SET #sql = CONCAT('SELECT (COUNT(*) > 0) INTO #result FROM ', tbl_name, ' WHERE Name = \'', tbl_column_new_value, '\'');
PREPARE stmt FROM #sql;
EXECUTE stmt;
SET msg = #result;
IF #result = 0 THEN
SET #sql = CONCAT('UPDATE ', tbl_name,
' SET Name = \'', tbl_column_new_value,
'\', Modified_Date = NOW(), Modified_by = ', tbl_user_id,
' WHERE Name = \'', tbl_column_old_value, ' \'');
PREPARE stmt FROM #sql;
EXECUTE stmt;
END IF;
DEALLOCATE PREPARE stmt;
END$$
DELIMITER ;
Here is SQLFiddle demo
It seems there is some error with the procedure syntax
wrong one:
1. SELECT Name from *tbl_name* where Name = tbl_column_new_value) then
SET msg := '1'
In the above line of code you didn't set any value to tal_name;
You can refer the below syntax concentrate on INTO in the query:
CREATE OR REPLACE PROCEDURE getDBUSERByUserId(
p_userid IN DBUSER.USER_ID%TYPE,
o_username OUT DBUSER.USERNAME%TYPE,
o_createdby OUT DBUSER.CREATED_BY%TYPE,
o_date OUT DBUSER.CREATED_DATE%TYPE)
IS
BEGIN
SELECT USERNAME , CREATED_BY, CREATED_DATE
INTO o_username, o_createdby, o_date
FROM DBUSER WHERE USER_ID = p_userid;
END;

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.

Categories