Joining multiple SELECT queries and some simple calculations into one query - java

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

Related

retrieve histogram from mssql table using java

I want to implement java application that can connect to any sql server and load any table from it. For each table I want to create histogram based on some arbitrary columns.
For example if I have this table
name profit
------------
name1 12
name2 14
name3 18
name4 13
I can create histogram with bin size 4 based on min and max value of profit column and count number of records for each bin.
result is:
profit count
---------------
12-16 3
16-20 1
My solution for this problem is retrieving all the data based on required columns and after that construct the bins and group by the records using java stream Collectors.groupingBy.
I'm not sure if my solution is optimized and for this I want some help to find the better algorithm specially when I have big number of records.(for example use some benefits of sql server or other frameworks that can be used.)
Can I use better algorithm for this issue?
edit 1:
assume my sql result is in List data
private String mySimpleHash(Object[] row, int index) {
StringBuilder hash = new StringBuilder();
for (int i = 0; i < row.length; i++)
if (i != index)
hash.append(row[i]).append(":");
return hash.toString();
}
//index is index of column for histogram
List<Object[]> histogramData = new ArrayList<>();
final Map<String, List<Object[]>> map = data.stream().collect(
Collectors.groupingBy(row -> mySimpleHash(Arrays.copyOfRange(row, index))));
for (final Map.Entry<String, List<Object[]>> entry : map.entrySet()) {
Object[] newRow = newData.get(rowNum);
double result = entry.getValue().stream()
.mapToDouble(row ->
Double.valueOf(row[index].toString())).count();
newRow[index] = result;
histogramData.add(newRow);
}
As you have considered, performing the aggregation after getting all the data out of SQL server is going to be very expensive if the number of rows in your tables increase. You can simply do the aggregation within SQL. Depending on how you are expressing your histogram bins, this is either trivial or requires some work. In your case, the requirement that the lowest bin start at min value requires a little bit of setup as opposed to binning starting from 0. See sample below. The inner query is mapping values to a bin number, the outer query is aggregating and computing the bin boundaries.
CREATE TABLE Test (
Name varchar(max) NOT NULL,
Profit int NOT NULL
)
INSERT Test(Name, Profit)
VALUES
('name1', 12),
('name2', 14),
('name3', 18),
('name4', 13)
DECLARE #minValue int = (SELECT MIN(Profit) FROM Test)
DECLARE #binSize int = 4
SELECT
(#minValue + #binSize * Bin) AS BinLow,
(#minValue + #binSize * Bin) + #binSize - 1 AS BinHigh,
COUNT(*) AS Count
FROM (
SELECT
((Profit - #minValue) / #binSize) AS Bin
FROM
Test
) AS t
GROUP BY Bin
| BinLow | BinHigh | Count |
|--------|---------|-------|
| 12 | 15 | 3 |
| 16 | 19 | 1 |
http://sqlfiddle.com/#!18/d093c/9

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.

PostgreSQL and JDBC: Is `UPDATE table ... RETURNING ... INTO` supported?

I have a table with [primary key counters] for [per page comments in another table].
These primary keys are per page: for each page, comment IDs start on 1.
I'd like to atomically allocate 10 IDs to write 10 new comments.
— Can I do this with PostgreSQL and JDBC?
(And do you have any links to any example / the relevant JDBC documentation?)
I've found only examples about how returning the primary key of a newly inserted row, using some getGeneratedKeys which doesn't seem useful in my case.
***
I think the SQL UPDATE statement would look something like this:
update PAGES
set NEXT_COMMENT_ID = NEXT_COMMENT_ID + 10
where PAGE_ID = ? <-- next-comment-id is *per page*
returning NEXT_COMMENT_ID into ?
So, different threads and servers won't attempt to reuse/overwrite the same IDs (right?).
This is supported without using the execute() and getResult() methods on the Statement object:
Something like this (barring any error handling):
String sql = "update ... returning ...";
boolean hasResult = statement.execute(sql);
int affectedRows = 0;
ResultSet rs = null;
if (hasResult) {
rs = statement.getResultSet();
}
int affectedRows = statement.getUpdateCount();
As you know what the statement does, this should be OK. Dealing with an "unknown" SQL statement is a bit more complicated because you need to call getMoreResults() and getUpdateCount() in a loop. See the Javadocs for details.
So you're wanting the following structure?:
x = page primary key
y = comments primary key
Page Table
x
-
1
2
3
4 etc
Comments Table
x y
- -
1 1
1 2
1 3
1 4
2 1
2 2
2 3
etc?
It would make most sense to have a foreign key structure here with an upper limit on the child records.
Creating a stored function that does update ... returning ... into works:
create or replace function INC_NEXT_PER_PAGE_REPLY_ID(
site_id varchar(32), page_id varchar(32), step int) returns int as $$
declare
next_id int;
begin
update DW1_PAGES
set NEXT_REPLY_ID = NEXT_REPLY_ID + step
where SITE_ID = site_id and PAGE_ID = page_id
returning NEXT_REPLY_ID into next_id;
return next_id;
end;
$$ language plpgsql;
And calling it like so:
statement = connection.prepareCall(
"{? = call INC_NEXT_PER_PAGE_REPLY_ID(?, ?, ?) }")
statement.registerOutParameter(1, java.sql.Types.INTEGER)
bind(values, statement, firstBindPos = 2) // bind pos no. 1 is the return value
statement.execute()
nextNewReplyIdAfterwards = statement.getInt(1)
Related documentation:
http://jdbc.postgresql.org/documentation/91/callproc.html
class CallableStatement and registerOutParameter()
StackOverflow question: How to return updated rows from function
To make the table contain a logical order then you may need to create a composite key and a foreign key within the child table.
sd=# create table x (x int);
CREATE TABLE
sd=# create table y (x int, y int);
CREATE TABLE
sd=# alter table x add primary key (x);
NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "x_pkey" for table "x"
ALTER TABLE
sd=# alter table y add foreign key (x) references x (x);
ALTER TABLE
sd=# alter table y add primary key (x,y);
NOTICE: ALTER TABLE / ADD PRIMARY KEY will create implicit index "y_pkey" for table "y"
ALTER TABLE
sd=# insert into x values (1);
INSERT 0 1
sd=# insert into x values (2);
INSERT 0 1
sd=# insert into x values (3);
INSERT 0 1
sd=# insert into y values (1,1);
INSERT 0 1
sd=# insert into y values (1,2);
INSERT 0 1
sd=# insert into y values (1,3);
INSERT 0 1
sd=# insert into y values (1,1);
ERROR: duplicate key value violates unique constraint "y_pkey"
DETAIL: Key (x, y)=(1, 1) already exists.
sd=# select * from x;
x
---
1
2
3
(3 rows)
sd=# select * from y;
x | y
---+---
1 | 1
1 | 2
1 | 3
(3 rows)
This should get you where you want to be?
You're doing an update but the statement generates results, so use executeQuery() instead of executeUpdate(). That's the real difference between the calls: executeQuery() deals with statements that yield a ResultSet; while executeUpdate() returns count of the number of rows affected.

Nested SQL Generator?

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).

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