I am using eclipse SQLQueryParserManager to extract table name and column names from a sql query. I am getting correct details if I use normal query. But when I use subquery within a query, I am not able to get details of subquery.
Here is what I have done :
SQLQueryParserManager parserManager = SQLQueryParserManagerProvider.getInstance()
.getParserManager("oracle", "10g");
SQLQueryParseResult parseResult = parserManager.parseQuery("select distinct xyz
from t_abc where type = 7 and transmiss_id in (select wxy from t_abc where
transmi_name like 'us_'");
QueryStatement resultObject = parseResult.getQueryStatement();
String parsedSQL = resultObject.getSQL();
List tableList = StatementHelper.getTablesForStatement(resultObject);
for (Object obj : tableList) {
TableInDatabase t = (TableInDatabase) obj;
System.out.println(" Tables : " + t.getName());
for (Object column : t.getValueExprColumns()) {
System.out.println(" Columns : " + ((ValueExpressionColumn)column).getName());
}
}
Can anyone suggest what I have to use , in order to get subquery result?
Related
I would like to update multiple rows of a table using Case clause in the update query.
I have an Map<String, String> which contains values of 2 columns. Key acts as the identifier of the row and the value in the map is the value of the column I want to update.
How can I do this in a spring data JPA #Query?
I would like to achieve something like
#Modifying
#Query("update RequestDetail rd set value = VALUE(:statusDetails) where name='Status' and RequestUuid=KEY(:statusDetails)")
void updateBulkStatus(Map<String, String> statusDetails);
But this is giving the exception - antlr.SemanticException: node did not reference a map.
Goal is to avoid multiple update queries to DB.
What better ways we have to update multiple rows with multiple values to a single column.
You will need to execute something like:
Update RequestDetail rd set rd.value = CASE WHEN (rd.name = :name1) THEN :value1 WHEN (rd.name = :name2) THEN :value2 WHEN (rd.name = :name3) THEN :value3 END
But you will not necessarily know how many items will be in the map, so you will need to generate your query text, like
String sql = "Update RequestDetail rd set CASE ";
int index = 1;
for (Map.Entry<String,String> entry : statusDetails.entrySet()) {
sql += " WHEN (rd.name = :name" + index + ") THEN :value" + (index++) + " ";
}
sql += " END";
You will also need to pass the parameters to your query.
I have the following section of code to implement a drop-down menu. Based on the two parameters selected from the menu, I run the corresponding query by replacing the selected column with the mapped column name.
String sql = "SELECT ?, ? FROM <table_name>";
ArrayList<Some_POJO> res = this.jdbcTemplate.query(sql, new ResultSetExtractor<Some_POJO>()
{//logic goes here.},
user_inputs_as_object_array);
Now, in the logic section, I'm using the following command to map the returned values:
while(rs.next()) {
Some_POJO = new SOME_POJO();
Some_POJO.setParam1(rs.getString("SOME_COLUMN_NAME")); //ERROR
}
Now, the marked line fails when I refer to column by name. But what is surprising is, the following workaround works:
while(rs.next()) {
Some_POJO = new SOME_POJO();
int i = 1;
Some_POJO.setParam1(rs.getString(i)); //Works(?!)
}
I tried to modify the JDBC template call to return the ResultSetMetaData object instead of an ArrayList of Some_POJO:
String sql = "SELECT ?, ? FROM <table_name>";
ResultSetMetaData res = this.jdbcTemplate.query(sql, new ResultSetExtractor<ResultSetMetaData>()
{//extractData now returns rs.getMetaData()},
user_inputs_as_object_array);
try
{
System.out.println("Column cout: " + res.getColumnCount());
for(int i = 1; i <= res.getColumnCount(); i++)
System.out.println("Label: " + res.getColumnLabel(i) + "\nName: " + res.getColumnName(i));
}
catch(SQLException sqle)
Only to get:
Column cout: 2
Label: :1
Name:
Label: :2
Name:
Could someone please shed some light into what is happening and (except for suggestions suggesting a complete redesign) what would be the most optimal way to achieve the expected functionality? I would prefer not to use column indexes since order changes would break the functionality.
Just for refactoring code, get values as:
ArrayList result = new ArrayList(0);
result.addAll(this.jdbcTemplate.query(sql, (RowMapper) (rs, rowNum) -> {
POJO out = new POJO();
out.setProperty1(rs.getString("prop1"));
out.setProperty2(rs.getString("prop2"));
...
return out;
}));
For me it works fine, passing field name in method, don't think that there are some troubles with spring jdbc. Maybe is something wrong in your sql query.
Using com.couchbase.client, java-client version 2.2.7 I have been unable to get a n1ql query working that uses an IN statement with multiple items see my example query and java code below
public int getCountForDuration(Long startTime, Long endTime, String ids){
JsonObject placeHolders = JsonObject.create().put("ids", ids).put("startTime", startTime).put("endTime", endTime);
N1qlQuery query = N1qlQuery.parameterized(COUNT_STATEMENT, placeHolders)
N1qlQueryResult result = bucket.query(query);
...
}
public static final String COUNT_STATEMENT = "select count(*) as count " +
"from bucketName " +
"where docType = 'docId' " +
"and (id IN [$ids]) " + <----- OFFENDING LINE
"and publishTimestamp between $startTime and $endTime";
I've tried setting ids using ('), ("), and (`) such as:
ids = "'123', '456'";
ids = "\"123\" , \"456\";
ids = "`123`,`456`";
None of these are working when there are multiple ids however if there is only one such as ids = "'123'" it works fine. Also my query works if I use it using CBQ on the terminal.
My question is this how do I crate a parameterized N1QL query which
can take multiple items in an IN statement?
Removing the brackets around the $ids in the statement and putting the actual ids into placeholders as a JsonArray object should work:
JsonObject placeHolders = JsonObject.create()
.put("ids", JsonArray.from("id1", "id2", "id3"))
.put("startTime", startTime)
.put("endTime", endTime);
Edit: Cleaning up by removing details not relevant to the problem.
The problem. JPA query returns no results.
String qstr = "select o from MyStats o where o.queue_name = :queue";
String queue = "3";
em.createQuery(qstr).setParameter("queue", queue);
I thought the problem was either in an incorrect syntax of the JPA query or in incorrect annotation of EmbeddedID. Hence I posted definitions of classes involved but told nothing about database table apart from that it was Oracle.
My test code: Read from DB, take first value and re-use that value in subsequent select query meaning that record exists. Should be there, it was just read, right?
Test
String queue = "";
String qstr1 = "select o from MyStats o";
String qstr2 = "select o from MyStats o where o.queue_name = :queue";
logger.debug("SQL query: " + qstr1);
List<MyStats> list = em.createQuery(qstr1).getResultList();
logger.debug("111 Returning results: " + list.size());
for (MyStats s : list) {
queue = s.getQueue_name();
logger.debug("Picking queue name: " + queue);
break;
}
logger.debug("SQL query: " + qstr2);
list = em.createQuery(qstr2).setParameter("queue", queue).getResultList();
logger.debug("222 Returning results: " + list.size());
Output:
SQL query: select o from MyStats o
111 Returning results: 166
Picking queue name: 3
SQL query: select o from MyStats o where o.rec_id.queue_name = :queue
222 Returning results: 0
Class definition
#Entity
public class MyStats {
private String queue_name;
private long stats_id;
... //getters and setters
}
A query without WHERE clause works correctly so as a query with a member of MyStats class.
em.createQuery("select o from MyStats o where o.stats_id = :sid").setParameter("sid", 179046583493L);
I am using Oracle 10 database, Java EE 5 SDK, Glassfish 2.1.
The problem appeared to be with the mapping of Java String type to database column CHAR type.
Database table queue_name column is defined as CHAR(20), while Java type is String.
There are few options to fix it
Replace database column CHAR type with VARCHAR
Pad query parameter value with spaces for every request
Use LIKE condition instead of equals = and add % to the end of parameter value
Speculative: Use cast
(1) Acceptable if you have control over the database table
(2) Works for the given select statement, possibly breaks for JOINs
(3) May fail to do the trick. LIKE 'a%' returns not only 'a ' but 'aa ', 'abc ', and so on
(4) This is not completely clear to me. I am not sure if it is possible to adopt:
em.createNativeQuery("select cast(queue_name as CHAR(20)) from ...");
I have several tables. I have a query also. My problem is to generate the SQL query dynamically using Java.
I have the following fields in a separate table:
Collumn name status
po_number, Y
unit_cost, Y
placed_date , Y
date_closed, Y
scheduled_arrival_date Y
date_closed Y
order_quantity Y
roll_number N
product_sku N
product_category_name N
rec_vendor_quantity Y
vendor_name Y
et_conversion_unit_quantity Y
from which i have to generate a query when the status is Y, the problem here is some time the above columns
The following query is the out put of the above :
here i have inculded all the columns but i have to exculde the column which has the status of N, please help me to construt the query using java.
select
pi.po_number,poi.unit_cost,pi.placed_date CreateDate,
case when isnull(pi.date_closed) then pi.scheduled_arrival_date
else pi.date_closed end as ReceviedDate,
poi.order_quantity,poi.roll_number,p.product_sku product_name,
pc.product_category_name,poi.rec_vendor_quantity,pv.vendor_name,p.et_conversion_unit_quantity,pi.note
from
purchase_order as pi,
purchase_order_inventory as poi,
product_vendors as pv,
products AS p,
product_categories AS pc
where
pi.purchase_order_id=poi.purchase_order_id and
pc.product_category_id=p.product_category_id and
poi.product_id = p.product_id and
poi.product_category_id=pc.product_category_id and
pi.vendor_id=pv.product_vendor_id and
( ( pi.date_closed >= '2012-01-01' and pi.date_closed <='2012-09-05 23:59:59' )
or ( pi.scheduled_arrival_date >= '2012-01-01' and pi.scheduled_arrival_date <='2012-09-05 23:59:59') ) and
pi.po_type=0
and pi.status_id = 0 and poi.transaction_type = 0
order by pi.po_number
UPDATE :
QUERY : STEP 1:
SELECT rcm.id,rcm.tablename,rcm.columnname,rcm.size,rcm.displayorder,rcm.isactive FROM report_customise_master rcm where rcm.tablename !='employee' and rcm.isactive='Y' order by rcm.displayorder;
STEP 2 :
Java method to construct the query :
public Map getComplexReportQuery() {
String query = "SELECT rcm.id,rcm.tablename,rcm.columnname,rcm.size,rcm.displayorder,rcm.isactive FROM report_customise_master rcm where rcm.tablename !='employee' and rcm.isactive='Y' order by rcm.displayorder;";
String tableName = "", from = "", select = "";
StringBuffer sb = new StringBuffer();
Map<String, List<String>> resultsMap = new LinkedHashMap<String, List<String>>();
Map<String, String> displayOrderMap = new LinkedHashMap<String, String>();
Map queryMap = new LinkedHashMap();
if (!query.isEmpty() || query.length() > 0) {
sb.append(query);
}
Connection connection = getConnection();
if (connection != null) {
try {
PreparedStatement reportQueryPS = connection.prepareStatement(sb.toString());
ResultSet reportQuery_rst = reportQueryPS.executeQuery();
List<String> tables = new ArrayList<String>();;
if (reportQuery_rst != null) {
StringBuilder selectQuery = new StringBuilder(" SELECT ");
StringBuilder fromQuery = new StringBuilder(" FROM ");
while (reportQuery_rst.next()) {
tableName = reportQuery_rst.getString("tablename");
List<String> columns = resultsMap.get(tableName);
if (columns == null) {
columns = new ArrayList<String>();
resultsMap.put(tableName, columns);
}
columns = resultsMap.get(tableName);
String columnName = reportQuery_rst.getString("columnname");
columns.add(columnName);
}
tableName = "";
for (Entry<String, List<String>> resultEntry : resultsMap.entrySet()) {
tableName = resultEntry.getKey();
List<String> columns = resultEntry.getValue();
int i = 0;
for (String column : columns) {
selectQuery.append(tableName + "." + column);
if (i != columns.size()) {
selectQuery.append(",");
} else {
selectQuery.append("");
}
i++;
}
if (!tables.contains(tableName)) {
tables.add(tableName);
}
}
//to remove comma at the end of line
select = selectQuery.toString().replaceAll(",$", "");
tableName = "";
int i = 0;
for (String table : tables) {
fromQuery.append(table);
fromQuery.append(" ");
fromQuery.append(table);
if (i != tables.size()) {
fromQuery.append(",");
} else {
fromQuery.append("");
}
i++;
}
from = fromQuery.toString().replaceAll(",$", "");
queryMap.put("query", select + from);
}
//from = from+"ORDER BY "+orderbyColumn+" "+sort+" ";
} catch (Exception ex) {
ex.printStackTrace();
} finally {
try {
closeConnection(connection, null, null);
} catch (Exception ex) {
ex.printStackTrace();
}
}
} else {
System.out.println("Connection not Established. Please Contact Vendor");
}
return queryMap;// return the map/ list which contains query and sory and display order
}
STEP 3 : Result query
{query= SELECT purchase_order.po_number,purchase_order.placed_date,purchase_order.date_closed,purchase_order.scheduled_arrival_date,purchase_order_inventory.unit_cost,purchase_order_inventory.order_quantity,purchase_order_inventory.roll_number,purchase_order_inventory.rec_vendor_quantity,products.product_sku,products.et_conversion_unit_quantity,product_categories.product_category_name ,product_vendors.vendor_name FROM purchase_order purchase_order,purchase_order_inventory purchase_order_inventory,products products,product_categories product_categories,product_vendors product_vendors}
but this not what i wanted, Please help me to construct the query i have given.
Two queries
You need to make two queries:
Query which fields are enabled
Build the second query string (the one you want to build dinamically)
It's this way because a SQL query has to tell which columns will be included before querying any data. In fact it will be used to build the internal DB query plan, it is, the way the DB motor will use to retrieve and organize the data you ask.
Query all columns
Is it necesary to query only that fields? Can't you query everything and use the relevant data?
Joins
Looking at the updated question I guess you need to dynamically add where conditions to join tables correctly. What I should do is have a reference telling me what coindition to add when a table is present.
There are at least two options:
Based on table pairs present (by example: "if A and B are present then add A.col1 = B.col2")
Based on tables present ("if B is present, then add A.col1 = B.col2; A should be present"
Based on your example I think the second option is more suitable (and easy to implement).
So I should have some static Map<String, JoinInfo> where JoinInfo has at least:
JoinInfo
+ conditionToAdd // by example "A.col1 = B.col2"
+ dependsOnTable // by example "A" to indicate that A must be present when B is present
So you can use:
that info to add tables that should be (by example: even if A has no selected cols, must be present to join with B)
include the conditionToAdd to the where clause
Anyway... I think you are getting into much trouble. Why so dynamic?
You have to approach the thing step by step.
Firstly you have to create a query that will return all rows that have status='Y'
Then you will put the COLUMN_NAME in a list of Strings.
List<String> list = new List<String>();
while(rs.next()){
list.add(rs.getString(columnNumber));
}
And then you have to loop the List and generate dynamically your second sql statement
String sqlSelect = "SELECT ";
String sqlFrom = " FROM SOME_OTHER_TABLE "
String sqlWhere = " WHERE SOME_CONDITION = 'SOME_VALUE' "
for(String x : list){
sqlFrom += x +" , "+;
}
//here make sure that you remove the last comma from sqlFrom because you will get an SQLException
String finalSql = sqlSelect + sqlFrom + sqlWhere ;