Nested SQL Generator? - java

I am trying to create a nested SELECT SQL statment. I store all value and id and want to select rows that satisfy multiple values. How can I generate a SELECT statement using Java? For example,
ID VALUE
1 RED
2 BIG
1 SMALL
1 SMOOTH
2 TALL
.....
To select an item that is both red and small the statement would be:
SELECT *
FROM table
WHERE table.value = RED AND
id IN (SELECT *
FROM table
WHERE table.value = SMALL AND id IN (...))

This type of problem is called Relational Division
SELECT ID
FROM tableName
WHERE VALUE IN ('SMALL','SMOOTH')
GROUP BY ID
HAVING COUNT(*) = 2
SQLFiddle Demo
the query above will result 1 since the ID contains both records.
If no unique constraint was enforce on value for every ID, DISTINCT is required.
SELECT ID
FROM tableName
WHERE VALUE IN ('SMALL','SMOOTH')
GROUP BY ID
HAVING COUNT(DISTINCT VALUE) = 2
SQLFiddle Demo
SQLFiddle Demo (with duplicate)
OTHER(s)
SQL of Relational Division

select ID
from MyTable
where VALUE in ('RED', 'SMALL')
group by ID
having count(distinct VALUE) = 2
SQL Fiddle Example
Results:
| ID |
------
| 1 |

Here is a general way to approach this:
select id
from t
group by id
having max(case when value = 'Red' then 1 else 0 end) = 1 and
max(case when value = 'Small' then 1 else 0 end) = 1
In other words, membership in the set becomes a clause in the having statement. These can be both inclusion and exclusion (use = 0 instead of = 1) and optional (use or instead of and).

Related

Oracle Query - Filter and Limit in two tables using JOIN

I am facing an issue in Oracle Query to achieve the following use case,
Consider I have two tables :
Table 1 : product
productId - Integer - primaryKey
productName - Varchar
Table 2 : product_sequence
productId - Integer - primaryKey
sequenceId - Integer - primaryKey
orderId - Integer
orderName - Varchar
product table has 1000 entries and product_sequence table has 10K entries
Requirement :
(paginate) fetch the entries from 0 to 100 / 100 to 200 / etc., in the product table
Distinct count of productId for showing the pagination in UI (check the sample query below)
Filter by 'productName' in 'product' table and 'orderName' in 'product_sequence' table
Query (tried) :
SELECT
p.productId, p.productName, ps.orderId, ps.orderName,
COUNT(distinct p.productId) OVER () AS TOTAL
FROM (
select *
from product
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
) p
JOIN product_sequence ps on p.productId=ps.productId
WHERE ps.orderId IN ('12','13','14');
NOTE : the above query will work in Oracle, But the issue is
Expected:
Return 100 entries from 'product' table with mapped entries in the 'product_sequence' table
Actual :
It first LIMITS 100 entries in product and then filter the orderId so the number of entries returned is reduced from 100 to lesser number
I agree my query is not correct: It first LIMIT by 100 in 'product' table in subquery and then goes for filter in second table which reduces the count
Could some one help me with the query for this please? Anyhelp is appreciated.
If my question is not clear, Let me know, I can explain with more info.
Try to move the OFFSET and FETCH clauses to the outer query, something like this:
SELECT q.productId, q.productName, q.orderId, q.orderName,
COUNT(distinct p.productId) OVER () AS TOTAL
FROM ( SELECT * FROM product p JOIN product_sequence ps ON p.productId = ps.productId
WHERE ps.orderId IN ('12','13','14') ) q
OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY
To get 100 rows per page "after filtering" you'll need to find all the productid values first, then process the main query.
For example:
select
p.productid, p.productname, ps.orderid, ps.orderName,
count(distinct p.productid) over() as total
from product p
join product_sequence ps on p.productid = ps.productid
where ps.orderid in ('12','13','14')
and p.productid in (
select *
from (
select distinct p.productid
from product p
join product_sequence ps on p.productid = ps.productid
where p.productname like '%theremin%' -- filter #1
and ps.orderid in ('12','13','14') -- filter #2
) x
order by productid
offset 300 rows -- get the 4th page
fetch first 100 rows only -- page size set to 100 rows
)
See example at db<>fiddle.

SQL query use to return latest ID from mysql database, not it returns 99999

My server would retrieve the latest ID from the database, now it is stuck and keeps returning the id 99999, even though the latest id is now 100040
My code is:
String insertTable = "SELECT * FROM dutyofcare ORDER BY Id DESC LIMIT 1";
ps = conn.prepareStatement(insertTable);
rs = ps.executeQuery();
String ResultS = "";
if (rs.next()) {
ResultS += rs.getString("Id");
}
The issue is that the ORDER BY in your query is doing a lexical (character-by-character) sort where 9 always comes after 1, and not numeric sort which handles the digit positions. This is because of the column type of ID. What you need is to ensure ID is a number before the sort is done.
Either change your ID to a numeric column type and run below query:
SELECT MAX(ID) from dutyofcare;
Or if you want to retain your column type (less efficient than above option):
select MAX(cast(ID AS UNSIGNED)) from dutyofcare;
Or if you want to retain your column type AND just fix your existing query (least efficient of all the options)
select * from dutyofcare order by CAST(ID AS UNSIGNED) desc limit 1;
All these methods basically treat the ID as number and choose the biggest value.

Joining multiple SELECT queries and some simple calculations into one query

I'm using MySQL and fetching a few different values from a table and then perform some basic math on it. Currently three seperate SELECT statements are in use and afterwards I perform some simple addition and subtraction with the outputs I get in Java.
I'm trying to optimize my code but sadly I gotta admit I'm a complete SQL noob. I'm pretty sure there's a way to join these select querys and the calculations so that I actually only get one output but I've not been able to find it.
My table looks something like this:
ID | value | inc | timestamp
--------------------------------------
0 | 5 | 4 | 2018-02-01 10:28:21
1 | 8 | 3 | 2018-02-01 10:28:47
...
My code currently looks like this:
int maxValue = MySQL.executeQuery("SELECT MAX(`value`) AS value FROM `table` where ID = idvalue AND `timestamp` >= TIMESTAMPADD(DAY,-3,NOW())");
int minValue = MySQL.executeQuery("SELECT MIN(`value`) AS value FROM `table` where ID = idvalue AND `timestamp` >= TIMESTAMPADD(DAY,-3,NOW())");
int minInc = MySQL.executeQuery("SELECT `inc` FROM `table` where ID = id AND value = minValue");
int output = maxValue - minValue + minInc;
Is there a way to shorten it to a single
int output = MYSQL.executeQuery( ??? );
?
Simply do select (select ...) - (select ...) + (select ...)
In your case, you can do( not tested in real environment )
select (SELECT MAX(`value`) AS value FROM `table` where ID = idvalue AND `timestamp` >= TIMESTAMPADD(DAY,-3,NOW())) - ( SELECT MIN(`value`) AS value FROM `table` where ID = idvalue AND `timestamp` >= TIMESTAMPADD(DAY,-3,NOW())) + (SELECT `inc` FROM `table` where ID = id AND value = minValue)
First off, there's something funky going on with the maxValue and minValue selects. The Max() and Min() operators will give you the max and min values of a given column of a given set of rows. Using one of these operators with such a specific where (by what seems to be a table's primary key) is probably not what you want to be doing.
Now, answering your question, I think you could do something along the lines of:
SELECT MAX('value') as max, MIN('value') as min
FROM `table` as t
WHERE ...
to "join" (careful with this word) the first 2 queries. This is simple select syntax: usually, there's no problem with selecting more than a column or an aggregate function at a time. Or, something like:
SELECT `inc`
FROM `table`
WHERE ID = id AND
value = (SELECT MIN('value') FROM 'table' WHERE ...)
to "join" the last two.
Single statement is possible using INNER JOIN since you are using a single able. Try this
SELECT MAX(`a.value`)-(MIN(`a.value`)+b.`inc`) AS Output
FROM `table` a
INNER JOIN `table` b ON a.ID=b.ID
AND a.ID = idvalue AND `a.timestamp` >= TIMESTAMPADD(a.DAY,-3,NOW())
AND b.value=(select MIN(value) from table WHERE ID=id);

Auto generate ID in java

I am developing a web application using JSP + Servlets and Oracle10 as back-end.
I have a table for storing information related to Customers:
|ID |Name |City |
|N0001 |ABC |NASIK |
|N0002 |PQR |NASIK |
|N.... |... |NASIK |
|N9999 |XYZ |NASIK |
|N10000 |LMN |NASIK |
|N10001 |MNO |NASIK |
In above table ID is a primary key, which is auto-generated depending upon the City (first Character of City + Number(Number must be minimum 4 character long, so for first ID, three leading zeroes will be added to number))
For Generating ID:
I am using following query for getting Largest ID from table, and then some java code Auto Generate Next ID.
query = "select ID from CUST where CITY='NASIK' order by ID desc";
Then getting the first ID from ResultSet which is as expected till the ID reach to the N9999, but when ID is N10000 or above then query is giving me N9999 as Largest ID.
Output after N9999:
ID
----------
N9999
N10001
N10000
N0002
N0001
Output Expecting
ID
----------
N10001
N10000
N9999
N0002
N0001
So my question is that is there anything wrong in above query? Or is there any better way to Auto Generate ID which contains String.
Edit1
My requirement is to auto generate ID which will contain character at starting position.
use this query
select ID from CUST where CITY='NASIK' order by to_number(substr(ID,2)) desc;
Since ID is a String not a number, it is sorted differently. it would sort as you wish if you had done sth like:
0001N
0002N
...
or stored only numbers, not Strings
With the INSERT, without inserting the prinary key ID, you can get the "generated keys."
String sql = "INSERT INTO Customers(Name, City) VALUES (?, ?)";
PreparedStatement stmt = conn.prepareStatement(sql,
PreparedStatement.RETURN_GENERATED_KEYS);
stmt.setString(1, ...);
stmt.setString(2, ...);
int affectedRows = stmt.executeUpdate();
// Get the ID:
String pk = "";
ResultSet keys = stmt.getGeneratedKeys();
if (keys.next()) {
pk = keys.getString(1);
}
It is so ugly, loop x loop, as one could have inserted more than one row, and the generated keys per row could be more than one.
As you can see, this is prove against concurrent parallel INSERTS.
About the sorting problem: you might go for a purely numerical ID, maybe a composed primary key CHAR(1), INT.
quick fix would be to increase the number of leading '0' after the character. But then the problem will occur later (e.g. 99999 and 100000).
Hence i would suggest to interpret the ID as a number from the second character on and do the order comparison upon that number value.

Get Insert Statement for existing row in sqlite android

I have database A for caching and B at the server, so I want to send a row from A to B and for that I need to generate the insert statement for already existed row in A.
below is what I want to accomplish
get insert select * from table where myId = 5;
and it should return
insert into table(myId,Col1,Col2,...) VALUES (5,'value1','value2',...);
I already had looked into this and this, that are not addressing my question.
Thanks in advance!
The sqlite3 command-line shell can generate such an output, but without column names, for queries:
sqlite> .mode insert MyTableName
sqlite> SELECT * FROM MyTable WHERE ID = 5;
INSERT INTO MyTableName VALUES(5,'value',NULL);
If you want the column names, you have to generate them manually:
SELECT 'INSERT INTO MyTable(a, b, c) VALUES (' ||
quote(a) || ',' ||
quote(b) || ',' ||
quote(c) || ');'
FROM MyTable
WHERE ID = 5;
--> INSERT INTO MyName(a, b, c) VALUES (42,'value',NULL);
(The same string operations could be done in Java.)
If your program does not know the exact database schema, you can read the list of columns for each table with PRAGMA table_info, and construct the statement from that:
> create table MyTable(myId, Col1, Col2, [...]);
> pragma table_info(MyTable);
cid name type notnull dflt_value pk
---------- ---------- ---------- ---------- ---------- ----------
0 myId 0 0
1 Col1 0 0
2 Col2 0 0
3 ... 0 0

Categories

Resources