jOOQ - how to use .whereNotExists() with conditional update? - java

I have a following jOOQ query, originally composed with the help of Lukas Eder in this question.
create.insertInto(DATA,DATA.TICKER,DATA.OPEN,DATA.HIGH,DATA.LOW,DATA.CLOSE,DATA.DATE)
.select(
select(
val(dailyData.getTicker()),
val(dailyData.getOpen()),
val(dailyData.getHigh()),
val(dailyData.getLow()),
val(dailyData.getClose()),
val(dailyData.getDate())
)
.whereNotExists(
selectOne()
.from(DATA)
.where(DATA.DATE.eq(dailyData.getDate()))
)
).execute();
This query works properly. In addition, I would like to modify to accomplish the following feat, but I am not certain it is actually doable. In simple english:
"Insert the row if a row with the same 'date' column doesn't already exist in the table. If it exists AND 'realtime close' column is true, update the 'close', otherwise do nothing."
The first part is already covered by the existing query, but the second part with if...update... is not and that's what I need help with.

In plain PostgreSQL, you would write this query as follows:
INSERT INTO data (ticker, open, high, low, close, date)
VALUES (:ticker, :open, :high, :low, :close, :date)
ON CONFLICT (date)
DO UPDATE SET close = false WHERE close
This translates to the following jOOQ query:
DSL.using(configuration)
.insertInto(DATA)
.columns(
DATA.TICKER,
DATA.OPEN,
DATA.HIGH,
DATA.LOW,
DATA.CLOSE,
DATA.DATE)
.values(
dailyData.getTicker(),
dailyData.getOpen(),
dailyData.getHigh(),
dailyData.getLow(),
dailyData.getClose(),
dailyData.getDate())
.onConflict()
.doUpdate()
.set(DATA.CLOSE, inline(false))
.where(DATA.CLOSE)
.execute();

Related

JOOQ timestampDiff(field_datetime1, field_datetime2) to set value in an update query not working

I'm using MYSQL and JOOQ and I'm trying to write an update query.
In this query I want to update field_datetime1 (type datetime) and also update field_timedifference (type bigint) with the time difference in milliseconds between field_datetime1 and field_datetime2.
How do I achive that with JOOQ?
I tried to write this code:
update(table)
.set(field_datetime1, now())
.set(field_timedifference, timestampDiff(field_datetime1, field_datetime2))
But it is not compiling, I get this error:
Cannot resolve method 'set(org.jooq.TableField<MyRecordType,java.lang.Long>, org.jooq.Field<org.jooq.types.DayToSecond>, org.jooq.TableField<MyRecordType,java.lang.Long>)'
I tried to wrap it in DSL.val and it does compile but it sends null in the query.
This is the query that I need to run, I tested it on MYSQL workbanch and it is exactly what I need:
update myTable
set field_timedifference =
TIMESTAMPDIFF(microsecond, field_datetime1, field_datetime2)
My coworker investigated and found an easy way to write it:
update(table)
.set(field_datetime1, now())
.set(field_timedifference, DSL.field("timestampDiff({0}, {1}, {2}", Long.class, DSL.keyword(DatePart.MICROSECOND.toSQL()), field_datetime1, field_datetime2))
This way jooq generates an actual query in the set :)

Update sql script dynamically

I'm working on requirement, where I have to run sql script. But behaviour of sql script is very dynamic because source table(DOD.PRODUCTS) is having dynamic schema. So when I merge that into target (BOB.PRODUCTS) in situation where one or more extra columns come in source then the below script should also be updated with new columns.
I'm looking for a way so that if new column arrives in source then how can I add the entry for that new column in below script at all places in most efficient way. My idea is just look for every position where column name needs to be added like in where clause, INSERT, VALUES etc. But am not happy wih this approach because its goona be very harsh code.
May I know any effective idea to update this script ? Code I can manage, I'm just looking for IDEA
MERGE INTO BOB.PRODUCTS GCA
USING (SELECT * FROM DOD.PRODUCTS) SCA
ON (SCA.CCOA_ID=GCA.CCOA_ID)
WHEN MATCHED THEN UPDATE SET
GCA.EFTV_TO=SYSDATE-1
,GCA.ROW_WRITTEN=CURRENT_TIMESTAMP
WHERE (GCA.EFTV_TO IS NULL)
AND(NVL(GCA.DESCR,'NULL')<>NVL(SCA.DESCR,'NULL')
OR NVL(GCA.SHORT_DESCR,'NULL')<>NVL(SCA.SHORT_DESCR,'NULL')
OR NVL(GCA.FREE_FRMT,'NULL')<>NVL(SCA.FREE_FRMT,'NULL')
OR NVL(GCA.CCOI_ATT,'NULL')<>NVL(SCA.CCOI_ATT,'NULL'))
WHEN NOT MATCHED THEN
INSERT(CCOA_ID, DESCR, SHORT_DESCR, FREE_FRMT, CCOI_ATT, EFTV_FROM, EFTV_TO, ROW_WRITTEN
)
VALUES(SCA.CCOA_ID, SCA.DESCR, SCA.SHORT_DESCR, SCA.FREE_FRMT, SCA.CCOI_ATT, SYSDATE, NULL, CURRENT_TIMESTAMP
);
INSERT INTO
BOB.PRODUCTS GCA(GCA.CCOA_ID, GCA.DESCR, GCA.SHORT_DESCR, GCA.FREE_FRMT, GCA.CCOI_ATT, GCA.EFTV_FROM, GCA.EFTV_TO, GCA.ROW_WRITTEN
)
SELECT SCA.CCOA_ID, SCA.DESCR, SCA.SHORT_DESCR, SCA.FREE_FRMT, SCA.CCOI_ATT, SYSDATE, NULL, CURRENT_TIMESTAMP
FROM DOD.PRODUCTS SCA
LEFT OUTER JOIN BOB.PRODUCTS GCA
ON NVL(SCA.CCOA_ID,'NULL')=NVL(GCA.CCOA_ID,'NULL')
AND NVL(SCA.DESCR,'NULL')=NVL(GCA.DESCR,'NULL')
AND NVL(SCA.SHORT_DESCR,'NULL')=NVL(GCA.SHORT_DESCR,'NULL')
AND NVL(SCA.FREE_FRMT,'NULL')=NVL(GCA.FREE_FRMT,'NULL')
AND NVL(SCA.CCOI_ATT,'NULL')=NVL(GCA.CCOI_ATT,'NULL')
WHERE NVL(SCA.DESCR,'NULL')<>NVL(GCA.DESCR,'NULL')
OR NVL(SCA.SHORT_DESCR,'NULL')<>NVL(GCA.SHORT_DESCR,'NULL')
OR NVL(SCA.FREE_FRMT,'NULL')<>NVL(GCA.FREE_FRMT,'NULL')
OR NVL(SCA.CCOI_ATT,'NULL')<>NVL(GCA.CCOI_ATT,'NULL');

Java update when data exists and insert if doesnt [duplicate]

In MySQL, if you specify ON DUPLICATE KEY UPDATE and a row is inserted that would cause a duplicate value in a UNIQUE index or PRIMARY KEY, an UPDATE of the old row is performed. For example, if column a is declared as UNIQUE and contains the value 1, the following two statements have identical effect:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
UPDATE table SET c=c+1 WHERE a=1;
I don't believe I've come across anything of the like in T-SQL. Does SQL Server offer anything comparable to MySQL's ON DUPLICATE KEY UPDATE?
I was surprised that none of the answers on this page contained an example of an actual query, so here you go:
A more complex example of inserting data and then handling duplicate
MERGE
INTO MyBigDB.dbo.METER_DATA WITH (HOLDLOCK) AS target
USING (SELECT
77748 AS rtu_id
,'12B096876' AS meter_id
,56112 AS meter_reading
,'20150602 00:20:11' AS time_local) AS source
(rtu_id, meter_id, meter_reading, time_local)
ON (target.rtu_id = source.rtu_id
AND target.time_local = source.time_local)
WHEN MATCHED
THEN UPDATE
SET meter_id = '12B096876'
,meter_reading = 56112
WHEN NOT MATCHED
THEN INSERT (rtu_id, meter_id, meter_reading, time_local)
VALUES (77748, '12B096876', 56112, '20150602 00:20:11');
There's no DUPLICATE KEY UPDATE equivalent, but MERGE and WHEN MATCHED might work for you
Inserting, Updating, and Deleting Data by Using MERGE
You can try the other way around. It does the same thing more or less.
UPDATE tablename
SET field1 = 'Test1',
field2 = 'Test2'
WHERE id = 1
IF ##ROWCOUNT = 0
INSERT INTO tablename
(id,
field1,
field2)
VALUES (1,
'Test1',
'Test2')
SQL Server 2008 has this feature, as part of TSQL.
See documentation on MERGE statement here - http://msdn.microsoft.com/en-us/library/bb510625.aspx
SQL server 2000 onwards has a concept of instead of triggers, which can accomplish the wanted functionality - although there will be a nasty trigger hiding behind the scenes.
Check the section "Insert or update?"
http://msdn.microsoft.com/en-us/library/aa224818(SQL.80).aspx

Jooq - Ignore Duplicates

I'm currently using Jooq for a project, but I need a way to ignore duplicate keys on insert.
I've got an array of objects I want to write into a table but if they already exist determined by a composite unique index on START_TS and EVENT_TYPE I want the insert to silently fail.
My Code looks something like this:
InsertValuesStep<MyRecord> query = fac.insertInto(MY_REC,
MY_REC.START_TS,
MY_REC.STOP_TS,
MY_REC.EVENT_DATA,
MY_REC.EVENT_TYPE,
MY_REC.PUBLISHED_TS,
MY_REC.MY_ID
);
for(int i=0;i<recs.length;i++)
{
MyClass evt = recs[i];
query.values(
new java.sql.Date(evt.startTS.getTime()),
(evt.stopTS == null) ? null : new java.sql.Date(evt.stopTS.getTime()),
evt.eventData,
evt.type.name(),
date,
id)
}
query.execute();
A solution like this would be ideal: https://stackoverflow.com/a/4920619/416338
I figure I need to add something like:
.onDuplicateKeyUpdate().set(MY_REC.EVENT_TYPE,MY_REC.EVENT_TYPE);
But whatever I add it still seems to throw an error on duplicates.
Support for MySQL's INSERT IGNORE INTO syntax is on the roadmap for jOOQ 2.3.0. This had been discussed recently on the jOOQ user group. This syntax will be simulated in all other SQL dialects that support the SQL MERGE statement.
In the mean time, as a workaround, you could try to insert one record at a time

How to invertly select columns in sql?

If I have a SQL table with columns:
NR_A, NR_B, NR_C, NR_D, R_A, R_B, R_C
and on runtime, I add columns following the column's sequence such that the next column above would be R_D followed by R_E.
My problem is I need to reset the values of columns that starts with R_ (labeled that way to indicate that it is resettable) back to 0 each time I re-run my script . NR_ columns btw are fixed, so it is simpler to just say something like:
UPDATE table set col = 0 where column name starts with 'NR_'
I know that is not a valid SQL but I think its the best way to state my problem.
Any thoughts?
EDIT: btw, I use postgres (if that would help) and java.
SQL doesn't support dynamically named columns or tables--your options are:
statically define column references
use dynamic SQL to generate & execute the query/queries
Java PreparedStatements do not insulate you from this--they have the same issue, just in Java.
Are you sure you have to add columns during normal operations? Dynamic datamodels are most of the time a realy bad idea. You will see locking and performance problems.
If you need a dynamic datamodel, take a look at key-value storage. PostgreSQL also has the extension hstore, check the contrib.
If you don't have many columns and you don't expect the schema to change, just list them explicitly.
UPDATE table SET NR_A=0;
UPDATE table SET NR_B=0;
UPDATE table SET NR_C=0;
UPDATE table SET NR_D=0;
Otherwise, a simple php script could dynamically build and execute your query:
<?php
$db = pg_connect("host=localhost port=5432 user=postgres password=mypass dbname=mydb");
if(!$db) die("Failed to connect");
$reset_cols = ["A","B","C","D"];
foreach ($col in $reset_cols) {
$sql = "UPDATE my_table SET NR_" . $col . "=0";
pg_query($db,$sql);
}
?>
You could also lookup table's columns in Postgresql by querying the information schema columns tables, but you'll likely need to write a plpgsql function to loop over the query results (one row per table column starting with "NR_").
if you rather using sql query script, you should try to get the all column based on given tablename.
maybe you could try this query to get all column based on given tablename to use in your query.
SELECT attname FROM
pg_attribute, pg_type
WHERE typname = 'tablename' --your table name
AND attrelid = typrelid
AND attname NOT IN ('cmin', 'cmax', 'ctid', 'oid', 'tableoid', 'xmin', 'xmax')
--note that this attname is sys column
the query would return all column with given tablename except system column

Categories