Flashback versions query - sql

The select statement returns two rows
id || description
1 TWO
2 TWO
I am kind of expecting three rows
id || description
1 ONE
1 TWO
2 TWO
according to the statement from the link which
reads It returns all the committed occurrences of the rows for a query of an object, while NOT displaying the UNCOMMITTED row versions.
The code is as follows :-
CREATE TABLE digits
(id NUMBER(2),
description VARCHAR2(15));
INSERT INTO digits VALUES (1,'ONE');
UPDATE digits SET description ='TWO' WHERE id=1;
INSERT INTO digits VALUES (2,'TWO');
COMMIT;
DELETE FROM digits;
SELECT id,description FROM digits
VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE;
The only reason i could think of is that if timestamp minvalue and maxvalue takes the DML timestamp value and not the DDL ones .....please throw some light on this !

The row you think is missing would come from this:
INSERT INTO digits VALUES (1,'ONE');
... but the data is never committed in that state because this:
UPDATE digits SET description ='TWO' WHERE id=1;
... occurred before your COMMIT. So that aligns with the statement you quoted, 1, ONE is not a committed occurrence of the row. There was never any point in time where another session could see those values.
If you look at the version data pseudocolumns you can see that both rows are seen as inserts with their current data:
CREATE TABLE digits (id NUMBER(2), description VARCHAR2(15));
EXEC dbms_lock.sleep(10);
INSERT INTO digits VALUES (1,'ONE');
UPDATE digits SET description ='TWO' WHERE id=1;
INSERT INTO digits VALUES (2,'TWO');
COMMIT;
SELECT id, description, versions_xid, versions_operation
FROM digits
VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE;
ID DESCRIPTION VERSIONS_XID V
---------- --------------- ---------------- -
2 TWO 08001B005C0D0100 I
1 TWO 08001B005C0D0100 I
If you commit between the first insert and update you can see the three rows and how they were modified:
CREATE TABLE digits (id NUMBER(2), description VARCHAR2(15));
EXEC dbms_lock.sleep(10);
INSERT INTO digits VALUES (1,'ONE');
COMMIT;
EXEC dbms_lock.sleep(10);
UPDATE digits SET description ='TWO' WHERE id=1;
INSERT INTO digits VALUES (2,'TWO');
COMMIT;
SELECT id, description, versions_xid, versions_operation
FROM digits
VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE;
ID DESCRIPTION VERSIONS_XID V
---------- --------------- ---------------- -
2 TWO 060018007C0C0100 I
1 TWO 060018007C0C0100 U
1 ONE 05000B00450C0100 I
I'm not entirely sure why the SLEEP calls are needed, but it doesn't quite work without them (the pseudocolumns are blank and only the current data is shown). I don't think I've ever seen a good explanation of that, but it isn't likely to be an issue in a real-world case.
From the documentation:
Specify BETWEEN TIMESTAMP ... to retrieve the versions of the row that
existed between two timestamps. Both expressions must evaluate to a
timestamp value and cannot evaluate to NULL. MINVALUE and MAXVALUE
resolve to the timestamp of the oldest and most recent data available,
respectively.
Normally MINVALUE would be restricted by the undo retention; since this is a new table it would go back to the point the table was created, which is less than the undo retention. You can't go back beyond that because it wouldn't make any sense: if you tried to use an explicit timestamp value before the table creation time, it would tell you the table structure had changed. The DML/DDL distinction you're making isn't really relevant though, you're only seeing two rows because of when you committed the data.

Related

Vertica sql overwrite data on insert

How to overwrite the table each time there is an insert statement in a vertica?
Consider:
INSERT INTO table1 VALUES ('My Value');
This will give say
| MyCol |
----------
MyValue
How to overwrite the same table on next insert statement say
INSERT INTO table1 VALUES ('My Value2');
| MyCol |
----------
MyValue2
You can either DELETE or TRUNCATE your table. There is no override method for Vertica. Use TRUNCATE since you have wanted only and only a value.
Source
INSERT INTO table1 VALUES ('My Value');
TRUNCATE TABLE table1;
INSERT INTO table1 VALUES ('My Value2');
Or (if connection get lost before you commit then it will not get effected.)
Rollback
An individual statement returns an ERROR message. In this case, Vertica rolls back the statement.
DDL errors, systemic failures, dead locks, and resource constraints return a ROLLBACK message. In this case, Vertica rolls back the entire transaction.
INSERT INTO table1 VALUES ('My Value');
DELETE FROM table1
WHERE MyCol !='My Value2';
INSERT INTO table1 VALUES ('My Value2');
COMMIT;
I might suggest that you don't do such a thing.
The simplest method is to populate the table with a row, perhaps:
insert into table1 (value)
values (null);
Then use update, not insert:
update table1
set value = ?;
That fixes your problem.
If you insist on using insert, you could insert values with an identity column and use a view to get the most recent value:
create table table1 (
table1_id identity(1, 1),
value varchar(255)
);
Then access the table using a view:
create view v_table1 as
select value
from table1
order by table1_id desc
limit 1;
If the view becomes inefficient, you can periodically empty the table.
One advantage of this approach is that the table is never empty and not locked for very long -- so it is generally available. Deleting rows and inserting rows can be tricky in that respect.
If you really like triggers, you can use a table as above. Then use a trigger to update the row in another table that has a single row. This also maximizes availability, without overhead for fetching the most recent value.
If it is a single-row table, then there's no risk whatsoever to fill it with a single row that can be NULL, as #Gordon Linoff suggests.
Internally, you should be aware that Vertica, in the background, always implements an UPDATE as a DELETE, by adding a delete vector for the row, and then applying an INSERT.
No problem with a single-row table, as the Tuple Mover (the background daemon process that wakes up all 5 mins to de-fragment the internal storage, to put it simply, and will create a single data (Read Optimized Storage - ROS) container out of: the previous value; the delete vector pointing to that previous value, thus deactivating it, and the newly inserted value that it is updated to.
So:
CREATE TABLE table1 (
mycol VARCHAR(16)
) UNSEGMENTED ALL NODES; -- a small table, replicate it across all nodes
-- now you have an empty table
-- for the following scenario, I assume you commit the changes every time, as other connected
-- processes will want to see the data you changed
-- then, only once:
INSERT INTO table1 VALUES(NULL::VARCHAR(16);
-- now, you get a ROS container for one row.
-- Later:
UPDATE table1 SET mycol='first value';
-- a DELETE vector is created to mark the initial "NULL" value as invalid
-- a new row is added to the ROS container with the value "first value"
-- Then, before 5 minutes have elapsed, you go:
UPDATE table1 SET mycol='second value';
-- another DELETE vector is created, in a new delete-vector-ROS-container,
-- to mark "first value" as invalid
-- another new row is added to a new ROS container, containing "second value"
-- Now 5 minutes have elapsed since the start, the Tuple Mover sees there's work to do,
-- and:
-- - it reads the ROS containers containing "NULL" and "first value"
-- - it reads the delete-vector-ROS containers marking both "NULL" and "first value"
-- as invalid
-- - it reads the last ROS container containing "second value"
-- --> and it finally merges all into a brand new ROS container, to only contain.
-- "second value", and, at the end the four other ROS containers are deleted.
With a single-row table, this works wonderfully. Don't do it like that for a billion rows.

Get identity of row inserted in Snowflake Datawarehouse

If I have a table with an auto-incrementing ID column, I'd like to be able to insert a row into that table, and get the ID of the row I just created. I know that generally, StackOverflow questions need some sort of code that was attempted or research effort, but I'm not sure where to begin with Snowflake. I've dug through their documentation and I've found nothing for this.
The best I could do so far is try result_scan() and last_query_id(), but these don't give me any relevant information about the row that was inserted, just confirmation that a row was inserted.
I believe what I'm asking for is along the lines of MS SQL Server's SCOPE_IDENTITY() function.
Is there a Snowflake equivalent function for MS SQL Server's SCOPE_IDENTITY()?
EDIT: for the sake of having code in here:
CREATE TABLE my_db..my_table
(
ROWID INT IDENTITY(1,1),
some_number INT,
a_time TIMESTAMP_LTZ(9),
b_time TIMESTAMP_LTZ(9),
more_data VARCHAR(10)
);
INSERT INTO my_db..my_table
(
some_number,
a_time,
more_data
)
VALUES
(1, my_time_value, some_data);
I want to get to that auto-increment ROWID for this row I just inserted.
NOTE: The answer below can be not 100% correct in some very rare cases, see the UPDATE section below
Original answer
Snowflake does not provide the equivalent of SCOPE_IDENTITY today.
However, you can exploit Snowflake's time travel to retrieve the maximum value of a column right after a given statement is executed.
Here's an example:
create or replace table x(rid int identity, num int);
insert into x(num) values(7);
insert into x(num) values(9);
-- you can insert rows in a separate transaction now to test it
select max(rid) from x AT(statement=>last_query_id());
----------+
MAX(RID) |
----------+
2 |
----------+
You can also save the last_query_id() into a variable if you want to access it later, e.g.
insert into x(num) values(5);
set qid = last_query_id();
...
select max(rid) from x AT(statement=>$qid);
Note - it will be usually correct, but if the user e.g. inserts a large value into rid manually, it might influence the result of this query.
UPDATE
Note, I realized the code above might rarely generate incorrect answer.
Since the execution order of various phases of a query in a distributed system like Snowflake can be non-deterministic, and Snowflake allows concurrent INSERT statements, the following might happen
Two queries, Q1 and Q2, do a simple single row INSERT, start at roughly the same time
Q1 starts, is a bit ahead
Q2 starts
Q1 creates a row with value 1 from the IDENTITY column
Q2 creates a row with value 2 from the IDENTITY column
Q2 gets ahead of Q1 - this is the key part
Q2 commits, is marked as finished at time T2
Q1 commits, is marked as finished at time T1
Note that T1 is later than T2. Now, when we try to do SELECT ... AT(statement=>Q1), we will see the state as-of T1, including all changes from statements before, hence including the value 2 from Q2. Which is not what we want.
The way around it could be to add a unique identifier to each INSERT (e.g. from a separate SEQUENCE object), and then use a MAX.
Sorry. Distributed transactions are hard :)
If I have a table with an auto-incrementing ID column, I'd like to be
able to insert a row into that table, and get the ID of the row I just
created.
FWIW, here's a slight variation of the current accepted answer (using Snowflake's 'Time Travel' feature) that gives any column values "of the row I just created." It applies to auto-incrementing sequences and more generally to any column configured with a default (e.g. CURRENT_TIMESTAMP() or UUID_STRING()). Further, I believe it avoids any inconsistencies associated with a second query utilizing MAX().
Assuming this table setup:
CREATE TABLE my_db.my_table
(
ROWID INT IDENTITY(1,1),
some_number INT,
a_time TIMESTAMP_LTZ(9),
b_time TIMESTAMP_LTZ(9),
more_data VARCHAR(10)
);
Make sure the 'Time Travel' feature (change_tracking) is enabled for this table with:
ALTER TABLE my_db.my_table SET change_tracking = true;
Perform the INSERT per usual:
INSERT INTO my_db.my_table
(
some_number,
a_time,
more_data
)
VALUES
(1, my_time_value, some_data);
Use the CHANGES clause with BEFORE(statement... and END(statement... specified as LAST_QUERY_ID() to SELECT the row(s) added to my_table which are the precise result of the previous INSERT statement (with column values that existed the moment the row(s) was(were) added, including any defaults):
SET insertQueryId=LAST_QUERY_ID();
SELECT
ROWID,
some_number,
a_time,
b_time,
more_data
FROM my_db.my_table
CHANGES(information => default)
BEFORE(statement => $insertQueryId)
END(statement => $insertQueryId);
For more information on the CHANGES, BEFORE, END clauses see the Snowflake documentation here.

Round off float in oracle 11g upto 2 places for all users

I want to insert float value into a column of type float in a table. But value coming from Cobol/Mainframe system after calculation is 5.4999999 but i want to insert 5.50 or 5.49 into oracle table without using round function. So is there any way to set a float value upto 2 decimal places for all the users in oracle 11g or like some setting done by SYSDBA for all the user or Default number value upto 2 or n number of decimal places ? Thanks in advance.
It's impossible to define a constant "format" in which people will always view numbers.
If you can't alter the table due to legacy issues the obvious thing to do would be to create a view on the table in order to transform the data into what you want. You could rename the original table and name the view the same as the original table in order to avoid changing all of your code. Test this first.
rename my_table to my_table_float
create or replace view my_table as
select <some_columns>
, cast(my_float_column as number(36, 2)) as my_float_column
from my_table_float
This solution strikes me as slightly dangerous - you'll need to know exactly what code uses the column and ensure that this code doesn't expect a float. Personally, I'm always more in favour of transforming the data as and when you need it rather than assuming it needs to be in a certain format before it's used.
No, there's no such thing as "setting a float value up to 2 decimal places for all users in Oracle".
You may either alter the target table column to number(*,2) or use the rounding.
You can create your table using a column defined as a number with precision and scale.
create table temp_foo (test number(4,2));
This means that column test will be a maximum of 4 digits in length, with 2 after the decimal place.
Then inserting the following...
insert into temp_foo values(54.345);
And then selecting...
select * from temp_foo;
Will result in a value of 54.35
If you want to allow a bigger number then increase the precision to say 10 and keep the scale at 2 - NUMBER(10,2) - now you can insert 12345678.234535 and you'll get 12345678.23 back out.
In summary, create your table to hold the data in the format your require. Let Oracle do the hard work :)
Hope that helps.
If you can't change the insert code to add a round() call, can't change the value being passed into the insert, and can't change the table structure... then you could use the view replacement Ben suggests; or adjust the data as it's queried directly form the table; or add a trigger to round the value as it is inserted:
create table t42 (id integer, value float);
-- value inserted with more precision than you want
insert into t42 values (1, 5.4999999);
create or replace trigger tr42
before insert or update on t42
for each row
begin
:new.value := round(:new.value, 2);
end;
/
-- value inserted with two decimal places of precision
insert into t42 values (2, 5.4999999);
select * from t42 order by id;
ID VALUE
---------- ----------
1 5.4999999
2 5.5
SQL Fiddle demo. Using a trigger for something like this seems like overkill, but if you've really exhausted all other avenues then it might be something to consider.
If you wanted to get 5.49 when 5.4999999 is inserted then you could use trunc(value, 2) instead:
create or replace trigger tr42
before insert or update on t42
for each row
begin
:new.value := trunc(:new.value, 2);
end;
/
insert into t42 values (3, 5.4999999)
/
select * from t42 order by id;
ID VALUE
---------- ----------
1 5.4999999
2 5.5
3 5.49

SQL Server Unique Composite Key of Two Field With Second Field Auto-Increment

I have the following problem, I want to have Composite Primary Key like:
PRIMARY KEY (`base`, `id`);
for which when I insert a base the id to be auto-incremented based on the previous id for the same base
Example:
base id
A 1
A 2
B 1
C 1
Is there a way when I say:
INSERT INTO table(base) VALUES ('A')
to insert a new record with id 3 because that is the next id for base 'A'?
The resulting table should be:
base id
A 1
A 2
B 1
C 1
A 3
Is it possible to do it on the DB exactly since if done programmatically it could cause racing conditions.
EDIT
The base currently represents a company, the id represents invoice number. There should be auto-incrementing invoice numbers for each company but there could be cases where two companies have invoices with the same number. Users logged with a company should be able to sort, filter and search by those invoice numbers.
Ever since someone posted a similar question, I've been pondering this. The first problem is that DBs don't provide "partitionable" sequences (that would restart/remember based on different keys). The second is that the SEQUENCE objects that are provided are geared around fast access, and can't be rolled back (ie, you will get gaps). This essentially this rules out using a built-in utility... meaning we have to roll our own.
The first thing we're going to need is a table to store our sequence numbers. This can be fairly simple:
CREATE TABLE Invoice_Sequence (base CHAR(1) PRIMARY KEY CLUSTERED,
invoiceNumber INTEGER);
In reality the base column should be a foreign-key reference to whatever table/id defines the business(es)/entities you're issuing invoices for. In this table, you want entries to be unique per issued-entity.
Next, you want a stored proc that will take a key (base) and spit out the next number in the sequence (invoiceNumber). The set of keys necessary will vary (ie, some invoice numbers must contain the year or full date of issue), but the base form for this situation is as follows:
CREATE PROCEDURE Next_Invoice_Number #baseKey CHAR(1),
#invoiceNumber INTEGER OUTPUT
AS MERGE INTO Invoice_Sequence Stored
USING (VALUES (#baseKey)) Incoming(base)
ON Incoming.base = Stored.base
WHEN MATCHED THEN UPDATE SET Stored.invoiceNumber = Stored.invoiceNumber + 1
WHEN NOT MATCHED BY TARGET THEN INSERT (base) VALUES(#baseKey)
OUTPUT INSERTED.invoiceNumber ;;
Note that:
You must run this in a serialized transaction
The transaction must be the same one that's inserting into the destination (invoice) table.
That's right, you'll still get blocking per-business when issuing invoice numbers. You can't avoid this if invoice numbers must be sequential, with no gaps - until the row is actually committed, it might be rolled back, meaning that the invoice number wouldn't have been issued.
Now, since you don't want to have to remember to call the procedure for the entry, wrap it up in a trigger:
CREATE TRIGGER Populate_Invoice_Number ON Invoice INSTEAD OF INSERT
AS
DECLARE #invoiceNumber INTEGER
BEGIN
EXEC Next_Invoice_Number Inserted.base, #invoiceNumber OUTPUT
INSERT INTO Invoice (base, invoiceNumber)
VALUES (Inserted.base, #invoiceNumber)
END
(obviously, you have more columns, including others that should be auto-populated - you'll need to fill them in)
...which you can then use by simply saying:
INSERT INTO Invoice (base) VALUES('A');
So what have we done? Mostly, all this work was about shrinking the number of rows locked by a transaction. Until this INSERT is committed, there are only two rows locked:
The row in Invoice_Sequence maintaining the sequence number
The row in Invoice for the new invoice.
All other rows for a particular base are free - they can be updated or queried at will (deleting information out of this kind of system tends to make accountants nervous). You probably need to decide what should happen when queries would normally include the pending invoice...
you can use the trigger for before insert and assign the next value by taking the max(id) with "base" filter which is "A" in this case.
That will give you the max(id) value as 2 and than increment it by max(id)+1. now push the new value to the "id" field. before insert.
I think this may help you
MSSQL Triggers: http://msdn.microsoft.com/en-in/library/ms189799.aspx
Test Table
CREATE TABLE MyTable
( base CHAR(1),
id INT
)
GO
Trigger Definition
CREATE TRIGGER dbo.tr_Populate_ID
ON dbo.MyTable
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO MyTable (base,id)
SELECT i.base, ISNULL(MAX(mt.id),0) +1 AS NextValue
FROM inserted i left join MyTable mt
on i.base = mt.base
GROUP BY i.base
END
Test
Execute the following statement multiple times and you will see the next values available in that group will be assigned to ID.
INSERT INTO MyTable VALUES
('A'),
('B'),
('C')
GO
SELECT * FROM MyTable
GO

Reference value of serial column in another column during same INSERT

I have a table with a SERIAL primary key, and also an ltree column, whose value I want to be the concatenation of those primary keys. e.g.
id | path
----------
1 1
2 1.2
3 1.2.3
4 1.4
5 1.5
I'm curious if there's a way to do such an insert in one query, e.g.
INSERT INTO foo (id, ltree) VALUES (DEFAULT, THIS.id::text)
I'm probably overreaching here and trying to do in one query what I should be doing in two (grouped in a transaction).
You could use a CTE to retrieve the value from the sequence once and use it repeatedly:
WITH cte AS (
SELECT nextval('foo_id_seq') AS id
)
INSERT INTO foo (id, ltree)
SELECT id, '1.' || id
FROM cte;
The CTE with a data-modifying command requires Postgres 9.1 or later.
If you are not sure about the name of the sequence, use
pg_get_serial_sequence() instead:
WITH i AS (
SELECT nextval(pg_get_serial_sequence('foo', 'id')) AS id
)
INSERT INTO foo (id, ltree)
SELECT id, '1.' || id
FROM i;
If the table name "foo" might not be unique across all schemas in the DB, schema-qualify it. And if the spelling of any name is non-standard, you have to double-quote:
pg_get_serial_sequence('"My_odd_Schema".foo', 'id')
Quick tests indicated #Mark's idea with lastval() might work too:
INSERT INTO foo (ltree) VALUES ('1.' || lastval());
You can just leave id out of the query, the serial column will be assigned automatically. Makes no difference.
There shouldn't be a race condition between rows. I quote the manual:
currval
Return the value most recently obtained by nextval for this sequence in the current session. (An error is reported if nextval has
never been called for this sequence in this session.) Because this is
returning a session-local value, it gives a predictable answer whether
or not other sessions have executed nextval since the current session
did.
This function requires USAGE or SELECT privilege on the sequence.
lastval
Return the value most recently returned by nextval in the current session. This function is identical to currval, except that instead of
taking the sequence name as an argument it refers to whichever
sequence nextval was most recently applied to in the current session.
It is an error to call lastval if nextval has not yet been called in
the current session.
This function requires USAGE or SELECT privilege on the last used sequence.
Bold emphasis mine.
But, as #Bernard commented, it can fail after all: there is no guarantee that the default value is filled (and nextval() called in the process) before lastval() is called to fill the 2nd column ltree. So stick with the first solution and nextval() to be sure.
This worked in my test:
INSERT INTO foo (id, ltree) VALUES (DEFAULT, (SELECT last_value from foo_id_seq));
I think there's a race condition there if two INSERTs are happening at the same time, since this references the last sequence value, instead of the current row. I would personally be more inclined to do this (pseudo-code):
my $id = SELECT nextval('foo_id_seq');
INSERT INTO foo (id, ltree) VALUES ($id, '$id');