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');
Related
I'm trying to write a java sql query, the simplified table would be table(name,version) with a unique constraint on (name, version).
I'm trying to insert a row into my database with a conditional statement. Meaning that when a entry with the same name exists, it should insert the row with same name and its version increased by 1.
I have tried with the following:
INSERT INTO table(name,version)
VALUES(?, CASE WHEN EXISTS(SELECT name from table where name=?)
THEN (SELECT MAX(version) FROM table WHERE name = ?) +1
ELSE 1 END)
values are sent by user.
My question is, how can I access the 'name' inside the values so I could compare them?
If you want to write this as a single query:
INSERT INTO table (name, version)
SELECT ?, COLAESCE(MAX(t2.version) + 1, 1)
FROM table t2
WHERE t2.name = ?;
That said, this is dangerous. Two threads could execute this query "at the same time" and possibly create the same version number. You can prevent this from happening by adding a unique index/constraint on (name, version).
With the unique index/constraint, one of the updates will fail if there is a conflict.
I see at least two approaches:
1. For each pair of name and version you first query the max version:
SELECT MAX(VERSION) as MAX FROM <table> WHERE NAME = <name>
And then you insert the result + 1 with a corresponding insert query:
INSERT INTO <table>(NAME,VERSION) VALUES (<name>,result+1)
This approach is very straight-forward, easy-to-read and implement, however, not really performant because of so many queries necessary.
You can achieve that with sql alone with sql analytics and window functions, e.g.:
SELECT NAME, ROW_NUMBER() over (partition BY NAME ORDER BY NAME) as VERSION FROM<table>
You can then save the result of this query as a table using CREATE TABLE as SELECT...
(The assumption here is that the first version is 1, if it is not the case, then one could slightly rework the query). This solution would be very performant even for large datasets.
You should get the name before insertion. In your case, if something went wrong then how would you know about it so you get the name before insert query.
Not sure but you try this:
declare int version;
if exists(SELECT name from table where name=?)
then
version = SELECT MAX(version) FROM table WHERE name = ?
version += 1
else
version = 1
end
Regards.
This is actually a bad plan, you might be changing what the user's specified data. That is likely to not be what is desired, maybe they're not trying to create a new version but just unaware that the one wanted already exists. But, you can create a function, which your java calls, not only inserts the requested version or max+1 if the requested version already exists. Moreover it returns the actual values inserted.
-- create table
create table nv( name text
, version integer
, constraint nv_uk unique (name, version)
);
-- function to create version or 1+max if requested exists
create or replace function new_version
( name_in text
, version_in integer
)
returns record
language plpgsql strict
as $$
declare
violated_constraint text;
return_name_version record;
begin
insert into nv(name,version)
values (name_in,version_in)
returning (name, version) into return_name_version;
return return_name_version;
exception
when unique_violation
then
GET STACKED DIAGNOSTICS violated_constraint = CONSTRAINT_NAME;
if violated_constraint like '%nv\_uk%'
then
insert into nv(name,version)
select name_in, 1+max(version)
from nv
where name = name_in
group by name_in
returning (name, version) into return_name_version;
return return_name_version;
end if;
end;
$$;
-- create some data
insert into nv(name,version)
select 'n1', gn
from generate_series( 1,3) gn ;
-- test insert existing
select new_version('n2',1);
select new_version('n1',1);
select *
from nv
order by name, version;
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();
I am retrieving data from database using jdbc. In my code I am using 3-4 tables to get data. But sometimes if table is not present in database my code gives exception. How to handle this situation. I want my code to continue working for other tables even if one table is not present. Please help.
I have wrote a code like this
sql="select * from table"
now Result set and all.
If table is not present in database it give exception that no such table. I want to handle it. In this code I cannot take tables which are already present in advance . I want to check here itself if table is there or not.
Please do not mark it as a duplicate question. The link you shared doesnot give me required answer as in that question they are executing queries in database not through JDBC code
For Sybase ASE the easiest/quickest method would consist of querying the sysobjects table in the database where you expect the (user-defined) table to reside:
select 1 from sysobjects where name = 'table-name' and type = 'U'
if a record is returned => table exists
if no record is returned => table does not exist
How you use the (above) query is up to you ...
return a 0/1-row result set to your client
assign a value to a #variable
place in a if [not] exists(...) construct
use in a case statement
If you know for a fact that there won't be any other object types (eg, proc, trigger, view, UDF) in the database with the name in question then you could also use the object_id() function, eg:
select object_id('table-name')
if you receive a number => the object exists
if you receive a NULL => the object does not exist
While object_id() will obtain an object's id from the sysobjects table, it does not check for the object type, eg, the (above) query will return a number if there's a stored proc named 'table-name'.
As with the select/sysobjects query, how you use the function call in your code is up to you (eg, result set, populate #variable, if [not] exists() construct, case statement).
So, addressing the additional details provided in the comments ...
Assuming you're submitting a single batch that needs to determine table existence prior to running the desired query(s):
-- if table exists, run query(s); obviously if table does not exist then query(s) is not run
if exists(select 1 from sysobjects where name = 'table-name' and type = 'U')
begin
execute("select * from table-name")
end
execute() is required to keep the optimizer from generating an error that the table does not exist, ie, the query is not parsed/compiled unless the execute() is actually invoked
If your application can be written to use multiple batches, something like the following should also work:
# application specific code; I don't work with java but the gist of the operation would be ...
run-query-in-db("select 1 from sysobjects where name = 'table-name' and type = 'U'")
if-query-returns-a-row
then
run-query-in-db("select * from table-name")
fi
This is the way of checking if the table exists and drop it:
IF EXISTS (
SELECT 1
FROM sysobjects
WHERE name = 'a_table'
AND type = 'U'
)
DROP TABLE a_table
GO
And this is how to check if a table exists and create it.
IF NOT EXISTS (
SELECT 1
FROM sysobjects
WHERE name = 'a_table'
AND type = 'U'
)
EXECUTE("CREATE TABLE a_table (
col1 int not null,
col2 int null
)")
GO
(They are different because in table-drop a temporary table gets created, so if you try to create a new one you will get an exception that it already exists)
Before running the query which has some risk in table not existing, run the following sql query and check if the number of results is >= 1. if it is >= 1 then you are safe to execute the normal query. otherwise, do something to handle this situation.
SELECT count(*)
FROM information_schema.TABLES
WHERE (TABLE_SCHEMA = 'your_db_name') AND (TABLE_NAME = 'name_of_table')
I am no expert in Sybase but take a look at this,
exec sp_tables '%', '%', 'master', "'TABLE'"
Sybase Admin
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
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