I'm running this script in MySQL to retrieve the count of appoinments that are grouped by a specific type and month:
SELECT Type, month(Start), COUNT(Type) FROM appointments
GROUP BY Type, month(Start);
Which provides me with this table:
[1]: https://i.stack.imgur.com/sRJwx.png
This is great! Now my problem is trying to place the data into variables:
String sql = "SELECT `Type`, month(`Start`), COUNT(Type) FROM appointments " +
"GROUP BY `Type`, month(`Start`)";
try(Statement s = DatabaseConnection.getConnection().createStatement();
ResultSet rs = s.executeQuery(sql)) {
while(rs.next()) {
String typeKey = rs.getString("Type");
int monthValue = rs.getInt("month(Start)");
int count = rs.getInt("COUNT(Type)");
I get an SQLException when I run this, saying that the column "month(Type) cannot be found. I have tried with the column name "Start" and tried with just "month" and recieve the same SQLException (I know, kind of a stupid troubleshoot but worth a shot). I've looked around for some solutions but I'm falling short.
This is for a school project, so I can only use JDBC and Java specific drivers. I'd appreciate it if someone could point me in the right direction.
Try rename columns with as
sql = "SELECT `Type` as type ,
month(`Start`) as mstart,
COUNT(Type) as ctype
FROM appointments " +
"GROUP BY `Type`, month(`Start`)";
Related
I have connected Java to SSMS and can call data from the server no problems using something like this:
String cell = "SELECT [Close] FROM ExcelData WHERE id_num = 3";
Statement st4 = con.createStatement();
ResultSet rs4 = st4.executeQuery(cell);
while (rs4.next())
{
Float close = rs4.getFloat("close");
System.out.format("%s\n", close);
}
When I replace the "SELECT [Close] FROM ExcelData WHERE id_num = 3" to "SELECT #SMA" I get the much questioned "Must declare the scalar variable #SMA.
I do not know how to do that.
Answering my own question to help others who may need to know this...
It cannot be done. The work around is to create a table in SQL and insert the variable into the table.
DROP TABLE IF EXISTS SMA
CREATE TABLE SMA (
SMA_Price DECIMAL (6,5),
)
INSERT INTO SMA VALUES (#SMA);
SELECT * FROM SMA;
You can then call "SELECT * FROM SMA" from Java.
I am having code something like this.
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
Calculation of fullTableName is something like:
public String getFullTableName(final String table) {
if (this.schemaDB != null) {
return this.schemaDB + "." + table;
}
return table;
}
Here schemaDB is the name of the environment(which can be changed over time) and table is the table name(which will be fixed).
Value for schemaDB is coming from an XML file which makes the query vulnerable to SQL injection.
Query: I am not sure how the table name can be used as a prepared statement(like the name used in this example), which is the 100% security measure against SQL injection.
Could anyone please suggest me, what could be the possible approach to deal with this?
Note: We can be migrated to DB2 in future so the solution should compatible with both Oracle and DB2(and if possible database independent).
JDBC, sort of unfortunately, does not allow you to make the table name a bound variable inside statements. (It has its reasons for this).
So you can not write, or achieve this kind of functionnality :
connection.prepareStatement("SELECT * FROM ? where id=?", "TUSERS", 123);
And have TUSER be bound to the table name of the statement.
Therefore, your only safe way forward is to validate the user input. The safest way, though, is not to validate it and allow user-input go through the DB, because from a security point of view, you can always count on a user being smarter than your validation.
Never trust a dynamic, user generated String, concatenated inside your statement.
So what is a safe validation pattern ?
Pattern 1 : prebuild safe queries
1) Create all your valid statements once and for all, in code.
Map<String, String> statementByTableName = new HashMap<>();
statementByTableName.put("table_1", "DELETE FROM table_1 where name= ?");
statementByTableName.put("table_2", "DELETE FROM table_2 where name= ?");
If need be, this creation itself can be made dynamic, with a select * from ALL_TABLES; statement. ALL_TABLES will return all the tables your SQL user has access to, and you can also get the table name, and schema name from this.
2) Select the statement inside the map
String unsafeUserContent = ...
String safeStatement = statementByTableName.get(usafeUserContent);
conn.prepareStatement(safeStatement, name);
See how the unsafeUserContent variable never reaches the DB.
3) Make some kind of policy, or unit test, that checks that all you statementByTableName are valid against your schemas for future evolutions of it, and that no table is missing.
Pattern 2 : double check
You can 1) validate that the user input is indeed a table name, using an injection free query (I'm typing pseudo sql code here, you'd have to adapt it to make it work cause I have no Oracle instance to actually check it works) :
select * FROM
(select schema_name || '.' || table_name as fullName FROM all_tables)
WHERE fullName = ?
And bind your fullName as a prepared statement variable here. If you have a result, then it is a valid table name. Then you can use this result to build a safe query.
Pattern 3
It's sort of a mix between 1 and 2.
You create a table that is named, e.g., "TABLES_ALLOWED_FOR_DELETION", and you statically populate it with all tables that are fit for deletion.
Then you make your validation step be
conn.prepareStatement(SELECT safe_table_name FROM TABLES_ALLOWED_FOR_DELETION WHERE table_name = ?", unsafeDynamicString);
If this has a result, then you execute the safe_table_name. For extra safety, this table should not be writable by the standard application user.
I somehow feel the first pattern is better.
You can avoid attack by checking your table name using regular expression:
if (fullTableName.matches("[_a-zA-Z0-9\\.]+")) {
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
}
It's impossible to inject SQL using such a restricted set of characters.
Also, we can escape any quotes from table name, and safely add it to our query:
fullTableName = StringEscapeUtils.escapeSql(fullTableName);
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
stmt.setString(1, addressName);
StringEscapeUtils comes with Apache's commons-lang library.
I think that the best approach is to create a set of possible table names and check for existance in this set before creating query.
Set<String> validTables=.... // prepare this set yourself
if(validTables.contains(fullTableName))
{
final PreparedStatement stmt = connection
.prepareStatement("delete from " + fullTableName
+ " where name= ?");
//and so on
}else{
// ooooh you nasty haker!
}
create table MYTAB(n number);
insert into MYTAB values(10);
commit;
select * from mytab;
N
10
create table TABS2DEL(tname varchar2(32));
insert into TABS2DEL values('MYTAB');
commit;
select * from TABS2DEL;
TNAME
MYTAB
create or replace procedure deltab(v in varchar2)
is
LvSQL varchar2(32767);
LvChk number;
begin
LvChk := 0;
begin
select count(1)
into LvChk
from TABS2DEL
where tname = v;
if LvChk = 0 then
raise_application_error(-20001, 'Input table name '||v||' is not a valid table name');
end if;
exception when others
then raise;
end;
LvSQL := 'delete from '||v||' where n = 10';
execute immediate LvSQL;
commit;
end deltab;
begin
deltab('MYTAB');
end;
select * from mytab;
no rows found
begin
deltab('InvalidTableName');
end;
ORA-20001: Input table name InvalidTableName is not a valid table name ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 21
ORA-06512: at "SQL_PHOYNSAMOMWLFRCCFWUMTBQWC.DELTAB", line 16
ORA-06512: at line 2
ORA-06512: at "SYS.DBMS_SQL", line 1721
I have some data in an SQLite Database. The following queries in SQLiteBrowser give me exactly the ResultSet I want:
With TempTable0 AS (SELECT Source, Date, Value AS Close FROM FinData WHERE Type = 'Close'),
TempTable1 AS (SELECT Source, Date, Value AS Colume FROM FinData WHERE Type = ' Colume')
SELECT DISTINCT TempTable0.Date, Close, Colume FROM TempTable0
JOIN TempTable1 ON TempTable1.Date = TempTable0.Date
Now, I tried to get this working as a single query via a simple executeQuery-Method from Java and read the data in a Matrixlike-DataStructure:
String sql = "With TempTable0 AS (SELECT Source, Date, Value AS Close FROM FinData WHERE Type = 'Close'), TempTable1 AS (SELECT Source, Date, Value AS Colume FROM FinData WHERE Type = ' Colume') SELECT DISTINCT TempTable0.Date, Close, Colume FROM TempTable0 JOIN TempTable1 ON TempTable1.Date = TempTable0.Date";
Connection Conn = DriverManager.getConnection(SQLCommunication.urlDB);
Statement stmt = Conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
while (rs.next() ) {
String res = rs.getString("Close");
DataMatrix.get("Close").add(res);
res = rs.getString("Colume");
DataMatrix.get("Colume").add(res);
}
However, the Resultset returns null.
I suspect this is because, it cannot work with two dependent SQLite-Queries, however, I have no idea, how to solve this.
I am no expert on SQLite and Java interaction and I am really running out of ideas, right now. Do you have any sugestions? (Even maybe a tip on nesting these two statements in one so it can be executed in one shot?)
Thanks so much!
Wiwi
I am using MySql database which has one table 'tradeinfo'.
Table structure:
Date TradeCode
2017.01.01 0001
2017.02.05 0002
2017.03.05 0001
My sql to find lastest trading day of the one tradecode is
SELECT TradeCode, MAX(date) most_recent_time FROM tradeinfo WHERE TradeCode = '0001'
I test the sql in Mysql db and can get right result which is "2017.03.05 0001"
But for my java code which is "lastestdbrecordsdate = rs.getDate("MOST_RECENT_TIME"); ", It ever return right result. But few days later, when run it again, I always get NULL.
My java code is:
Connection con = DriverManager.getConnection("jdbc:mysql://...",user,password);
String sqlstatement = "SELECT TradeCode, MAX(date) most_recent_time FROM tradeinfo WHERE TradeCode = '0001' ";
PreparedStatement sqlstat = con.prepareStatement(sqlstatement);
ResultSet rsquery = sqlstat.executeQuery(sqlstatement);
CachedRowSetImpl cachedRS = new CachedRowSetImpl();
cachedRS.populate(rsquery);
while(cachedRS.next() ) {
System.out.println(cachedRS.getMetaData().getColumnCount());
Date lastestdbrecordsdate = cachedRS.getDate("MOST_RECENT_TIME");
}
Is the problem that I config the mysql wrongly or I write wrong java code?
Thanks all!
You have several problems here. First, you should be using the following query:
SELECT MAX(date) most_recent_time FROM tradeinfo WHERE TradeCode = '0001'
Adding TradeCode to the select list doesn't make any sense because it is not an aggregate, but rather each record has a value for this column.
With regard to why you are getting null results, you need to call ResultSet#next() to advance the cursor to the first line:
Connection con = DriverManager.getConnection("jdbc:mysql://...", user, password);
Statement sqlstat = con.prepareStatement(sqlstatement);
ResultSet rsquery = sqlstat.executeQuery(); // DON'T pass anything to executeQuery()
if (rsquery.next()) {
Date lastestdbrecordsdate = rs.getDate("most_recent_time");
}
Another problem I just noticed is that you were passing in the query string to your call to Statement#executeQuery(). This is wrong, and you should not be passing anything to this method.
I have a table on my jsp page that will have a column populated by a database column with type CLOB. I am running into some trouble doing this, and have seen other questions asked about this, but the answers have not worked for me. Here is my statement where comments is a CLOB.
stmt = conn.prepareStatement("SELECT DISTINCT restriction, person, start_date, end_date, comments "
+ " FROM restrictions WHERE person = ? "
+ " AND (start_date BETWEEN TO_DATE (? , 'yyyy/mm/dd') AND TO_DATE (? , 'yyyy/mm/dd') "
+ " OR start_date < TO_DATE (? , 'yyyy/mm/dd') AND end_date IS NULL) " );
stmt.setString(1, Id);
stmt.setString(2, StartRest);
stmt.setString(3, EndRest);
stmt.setString(4, EndRest);
result = stmt.executeQuery();
And then I will have the columns in a while loop:
while (result.next()) {
restrictions = StringUtils.defaultString(result.getString("str_restriction"));
.......
// here is where I would get my Clob data from the query.
So, basically, I was wondering if there is a way to translate the CLOB in the query, or even in the java code, so it would be usable in my page.
The problem comes from the distint clause of the query, which can't be applied to a CLOB.
Check if the distinct keyword is really needed. Or maybe you could rewrite your query as
select restriction, person, start_date, end_date, comments from restrictions
where id in (select distinct id from restrictions where <original where clause>)
PS: next time, include the error message and your database in the question. I've been able to find the problem with a simple google search on "ora-00932 clob".