HSQLDB - updatable statements don't work with "SELECT TOP" or "ORDER BY" - hsqldb

I'm using HSQLDB and preparedStatements just fine, but if I include either "SELECT TOP" or "ORDER BY" in my SQL statement, when I call updateBoolean (or UpdateInt, etc), I hit an exception:
java.sql.SQLException: attempt to assign to non-updatable column
This sample code works fine:
preparedStatement = connection.prepareUpdatable(
"SELECT " + MyTable.COL_ID + ", " +
MyTable.COL_READ +
" FROM " + MyTable.NAME +
" WHERE " + MyTable.COL_LOCAL +
" =? AND " + MyTable.COL_REMOTE +
" =?",
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
preparedStatement.setString(1, localAddress);
preparedStatement.setString(2, remoteAddress);
ResultSet rs = connection.query(preparedStatement);
if (rs.next())
{
rs.updateBoolean(MyTable.COL_READ, isRead);
rs.updateRow();
}
I get the exception if I change "SELECT" to "SELECT TOP". Or if I append this to the SQL statement:
" ORDER BY " + MyTable.COL_RECEIVED_TIMESTAMP + " DESC"
Thanks for any help.
NickB

An updatable SELECT statement cannot have TOP n, LIMIT or ORDER BY. This restriction is imposed by the SQL standard. Your SELECT becomes not-updatable when you add one of those keywords.
It is possible to use a subquery in a WITH clause with the above keywords and the SELECT is updatable.
CREATE TABLE t (a int, b int, PRIMARY KEY(a));
WITH SUBQ(COL) AS (SELECT TOP 1 a FROM t)
SELECT * FROM t WHERE a IN (SELECT * FROM SUBQ)

Related

how can join two query in PostgreSQL

I want to join two querys into one query.
What retrieved in the first query is a tables with column of resourceindex that sorts ascending:
String loadRates = "SELECT * FROM ratings WHERE userindex="
+ uindex
+ " ORDER BY rank DESC";
And in the second query, what should retrieved is rows of resourceindexes:
String loadResources = "SELECT * FROM resourceinfo WHERE resourceindex = "
+ rs.getInt("resourceindex");
How can I combine these into a single query?
Do not use old style join but use the keyword join.
Never ever write an SQL string like that with concatenation of parameters but use parameters instead.
"SELECT * FROM public.resourceinfo"
+ " inner join public.ratings ON ratings.resourceindex = resourceinfo.index"
+ " WHERE ratings.userindex = $1" +
+ " ORDER BY ratings.rank DESC;";
How you would apply the parameters depend on the language you are using which you didn't tag.
EDIT: If you meant it would also filtered by a resourceindex parameter then add it too as:
AND resourceinfo.index = $2

Using sub-select in FROM clause inside JPA #NamedQuery

In my app I need to use #NamedQuery to find the type of the most frequent operation assigned to specific account
#Entity
#Table(name="\"ACCOUNTOPERATION\"")
#NamedQuery(name="AccountOperation.findTypeOfMostFrequentOperation", query="" +
"SELECT ao.type from AccountOperation ao WHERE ao.account.id = ?1 " +
"GROUP BY ao.type HAVING COUNT(ao) = (" +
"SELECT MAX(typeCountQuery.typeCount) " +
"FROM (" +
"SELECT COUNT(aop) as typeCount " +
"FROM AccountOperation aop WHERE aop.account.id = ?1 GROUP BY aop.type" +
") as typeCountQuery" +
")"
)
public class AccountOperation {
#ManyToOne
private Account account;
private BigDecimal amount;
private OperationType type;
...
Right after FROM clause at '(' character, which begins typeCountQuery's body I'm getting
')', ',', GROUP, HAVING, IN, WHERE or identifier expected, got '('
I've read that JPA does not support sub-selects in the FROM clause, so is there any way to rewrite SQL code to still use it in #NamedQuery?
I'm using IntelliJ IDE with H2 DB and with eclipselink and javax.persistence in dependencies.
Link to source
In JPQL, you cannot use subqueries. To resolve this issue, you need to use some keywords like ALL, ANY, which work similiar.
So in your situation it could be:
#NamedQuery(name="AccountOperation.findTypeOfMostFrequentOperation", query="" +
"SELECT ao.type from AccountOperation ao WHERE ao.account.id = ?1 " +
"GROUP BY ao.type HAVING COUNT(ao) >= ALL (" +
"SELECT COUNT(aop) as typeCount " +
"FROM AccountOperation aop WHERE aop.account.id = ?1 GROUP BY aop.type)"
The type with a highest count returns the following query
select type
from AccountOperation
where id = ?
group by type
order by count(*) desc
fetch first 1 ROWS only
You should be anyway avare of the existence of ties, i.e. more types with the identical maximal count and should make some thought how to handle them.
I.e. in Oracle you may say fetch first 1 ROWS WITH TIES to get all the types with tha maximal count.

execute variable values Google BigQuery

I am trying to execute the value of the variable, but I can't find documentation about it in Google BigQuery.
DECLARE SQL STRING;
SELECT
SQL =
CONCAT(
"CREATE TABLE IF NOT EXISTS first.rdds_",
REPLACE(CAST(T.actime AS STRING), " 00:00:00+00", ""),
" PARTITION BY actime ",
" CLUSTER BY id ",
" OPTIONS( ",
" partition_expiration_days=365 ",
" ) ",
" AS ",
"SELECT * ",
"FROM first.rdds AS rd ",
"WHERE rd.actime = ",
"'", CAST(T.actime AS STRING), "'",
" AND ",
"EXISTS ( ",
"SELECT 1 ",
"FROM first.rdds_load AS rd_load ",
"WHERE rd_load.id= rd.id ",
")"
) AS SQ
FROM (
SELECT DISTINCT actime
FROM first.rdds AS rd
WHERE EXISTS (
SELECT 1
FROM first.rdds_load AS rd_load
WHERE rd_load.id= rd.id
)
) T;
My variable will have many rows with scripted for create tables and I need to execute this variable.
In SQL Server for to execute variable is:
EXEC(#variable);
How to I execute SQL variable in Google BigQuery?
EDIT:
I did new test with version beta:
Using array, all rows in one result (ARRAY_AGG):
DECLARE SQL ARRAY<STRING>;
SET SQL = (
SELECT
CONCAT(
"CREATE TABLE IF NOT EXISTS first.rdds_",
REPLACE(CAST(T.actime AS STRING), " 00:00:00+00", ""),
" PARTITION BY actime ",
" CLUSTER BY id ",
" OPTIONS( ",
" partition_expiration_days=365 ",
" ) ",
" AS ",
"SELECT * ",
"FROM first.rdds AS rd ",
"WHERE rd.actime = ",
"'", CAST(T.actime AS STRING), "'",
" AND ",
"EXISTS ( ",
"SELECT 1 ",
"FROM first.rdds_load AS rd_load ",
"WHERE rd_load.id= rd.id ",
")"
)
) AS SQ
FROM (
SELECT DISTINCT actime
FROM first.rdds AS rd
WHERE EXISTS (
SELECT 1
FROM first.rdds_load AS rd_load
WHERE rd_load.id= rd.id
)
) T
);
My result:
One row with all instructions. But I can't running this with all instructions
Update: as of 5/20/2020, BigQuery released dynamic SQL feature for you to achieve the goal.
Dynamic SQL is now available as a beta release in all BigQuery regions. Dynamic SQL lets you generate and execute SQL statements dynamically at runtime. For more information, see EXECUTE IMMEDIATE.
x
================
BigQuery does not support this (Dynamic SQL) in pure SQL, but you can implement this in any client of your choice
While Mikhail is correct that this historically hasn't been supported in BigQuery, the very new beta release of BigQuery Scripting should let you accomplish similar results:
https://cloud.google.com/bigquery/docs/reference/standard-sql/scripting
In this case, you would need to use SET to assign the value of the variable, and there isn't an EXEC statement at this time, but there is support for conditionals, loops, variables, etc.
To recreate your example, you could store the results of a query against either your first.rdds_load table, then use WHILE to loop over those results. Within that loop, you can run a normal CREATE TABLE if it doesn't already exist. I'm thinking something along these lines based on your example . . .
DECLARE results ARRAY<STRING>;
DECLARE i INT64 DEFAULT 1;
DECLARE cnt INT64 DEFAULT 0;
SET results = ARRAY(
SELECT
DISTINCT AS VALUE
CAST(actime AS STRING)
FROM
first.rdds AS rd
WHERE
EXISTS (
SELECT
1
FROM
first.rdds_load AS rd_load
WHERE
rd_load.id = rd.id
)
);
SET cnt = ARRAY_LENGTH(results);
WHILE i <= cnt DO
/* Body of CREATE TABLE goes here; you can access the rows from the query above using results[ORDINAL(i)] as you loop through*/
END WHILE;
There's also support for stored procedures, which can be executed via CALL with passed arguments, which may work in your case as well (if you need to abstract the creation logic used by many scripts).
(I would argue that this scripting support is superior to building and executing strings, since you'll still get SQL validation and such for your query.)
As always with beta features, use with caution in production—but for what it's worth, thus far my experience has been incredibly stable.

My C# SQL Select statement is not picking up any records in my table

I have 4 records in my SQL table that and I'm doing a SQL select statement to select records based on a certain criteria but I'm not picking any records up. Can someone please help?
Here is my SELECT statement:
string Sql = "";
Sql = #"SELECT * FROM " + _dtlName + "
WHERE Shipper_No = '" + sidNo + "'
AND Job_Code != '" + "R" + "'";
I have 3 records that have a Null for Job_Code and 1 record that has an R.
Based on that, I should pickup the 3 records with the NULL Job_Code but it returns 0 records.
I suspect the problem is that a comparison between any non-null value and a null value doesn't return a value of true or false, but null. So your query should probably be:
string sql = "SELECT * FROM " + _dtlName + " WHERE Shipper_No = #ShipperNo " +
"AND (Job_Code IS NULL OR Job_Code != 'R')";
(Note that I've extracted a parameter for Shipper_No - you shouldn't be including values directly in your SQL like that. Obviously you'll need to then set the parameter value in the SQL command.)
Also note that <> is more common in SQL to represent "not equal to", but I believe T-SQL allows either form.
Try,
Sql = #"SELECT * FROM " + _dtlName + " WHERE Shipper_No = '" + sidNo + "' AND Job_Code NOT IN ('R')";
I think your query should be more like this since you want to select the NULL values.
string Sql = String.Format("SELECT * " +
"FROM {0} " +
"WHERE Shipper_No = {1} AND " +
" Job_Code IS NULL",
_dtlName, sidNo)

How to combine asterix syntax with table abbreviations

Is it possible to combine * syntax with table abbreviations?
I want to do something like:
"SELECT subfunds.* FROM subfunds S" +
" INNER JOIN funds F ON S.id_fund = F.id" +
" WHERE F.fund_short IN('" + stSQLFundList + "')"
The above code gets a syntax error
"invalid reference to FROM-clause entry for table "subfunds".
I already found that if I do
"SELECT * FROM subfunds S" +
" INNER JOIN funds F ON S.id_fund = F.id" +
" WHERE F.fund_short IN('" + stSQLFundList + "')"
then I get all fields from both tables rather than from the subfunds table only.
So how do I get all fields from the first table (and none of the other tables' fields) in my answer set while also being able to use the single-letter table abbreviations?
Change your code to this and you will get all fields from subfunds.
"SELECT S.* FROM subfunds S" +
" INNER JOIN funds F ON S.id_fund = F.id" +
" WHERE F.fund_short IN('" + stSQLFundList + "')"
If you are using an alias, then you want to reference that table by it's alias.