Using SQLite's new WITH RECURSIVE CTE clause - sql

SQLite 3.8.3 added support for CTEs. I tried some of the sample CTEs on this page and they work fine. However, after reading the documentation and trying to adapt some of the examples I am unable to create a simple test.
First, I create a simple table with two fields: id and parent. This will create a simple tree or linked list of records:
CREATE TABLE test(id INTEGER PRIMARY KEY ASC,parent INTEGER);
Now I populate it with a few rows:
INSERT INTO test (parent) VALUES (NULL);
INSERT INTO test (parent) VALUES (1);
INSERT INTO test (parent) VALUES (2);
INSERT INTO test (parent) VALUES (3);
After which I have a table that looks like this:
---+-------
id | parent
---+-------
1 | NULL
2 | 1
3 | 2
4 | 3
Now I want to generate a list of rows along the path between 3 and 1:
WITH RECURSIVE test1(id,parent) AS (
VALUES(3,2)
UNION ALL
SELECT * FROM test WHERE test.parent=test1.id)
SELECT * FROM test1;
But I get the error:
no such column: test1.id
Both test and test1 have an id field, so why does it claim it does not exist? I have reviewed the documentation several times and don't see my mistake. What am I doing wrong?

It is necessary to include the test1 table in the SELECT:
WITH RECURSIVE test1(id,parent) AS (
VALUES(3,2)
UNION ALL
SELECT test.id,test.parent FROM test,test1 WHERE test1.parent=test.id)
SELECT * FROM test1;
Note that the WHERE clause has been reversed, the original test in the question returns from the current row to the end, rather than from the end back to the start.

Related

How to Insert new Record into Table if the Record is not Present in the Table in Teradata

I want to insert a new record if the record is not present in the table
For that I am using below query in Teradata
INSERT INTO sample(id, name) VALUES('12','rao')
WHERE NOT EXISTS (SELECT id FROM sample WHERE id = '12');
When I execute the above query I am getting below error.
WHERE NOT EXISTS
Failure 3706 Syntax error: expected something between ')' and the 'WHERE' keyword.
Can anyone help with the above issue. It will be very helpful.
You can use INSERT INTO ... SELECT ... as follows:
INSERT INTO sample(id,name)
select '12','rao'
WHERE NOT EXISTS (SELECT id FROM sample WHERE id = '12');
You can also create the primary/unique key on id column to avoid inserting duplicate data in id column.
I would advise writing the query as:
INSERT INTO sample (id, name)
SELECT id, name
FROM (SELECT 12 as id, 'rao' as name) x
WHERE NOT EXISTS (SELECT 1 FROM sample s WHERE s.id = x.id);
This means that you do not need to repeat the constant value -- such repetition can be a cause of errors in queries. Note that I removed the single quotes. id looks like a number so treat it as a number.
The uniqueness of ids is usually handled using a unique constraint or index:
alter table sample add constraint unq_sample_id unique (id);
This makes sure that the database ensures uniqueness. Your approach can fail if two inserts are run at the same time with the same id. An attempt to insert a duplicates returns an error (which the exists can then avoid).
In practice, id columns are usually generated automatically by the database. So the create table statement would look more like:
id integer generated by default as identity
And the insert would look like:
insert into sample (name)
values (name);
If id is the Primary Index of the table you can use MERGE:
merge into sample as tgt
using VALUES('12','rao') as src (id, name)
on src.id = tgt.id
when not matched
then insert (src.id,src.name)

How to do a Cross column unique constraint in SQL (Oracle)

How to have a Unique Constraint in Oracle-DB with two columns so that a duplicate must not occur in one or the other.
Assume this table
|id | A | B |
|---|---|---|
| 1 | 1 | 2 |
| 2 | 3 | 4 |
I that a new row is not allowed to have in column "A" a value that duplicate a value from column "A" or "B".
In the example above: I am allowed to add 5 to column "A" but not 1, 2, 3, or 4.
My idea was to do something like:
CREATE UNIQUE INDEX crossTest ON test (
SELECT t.A AS x FROM test t
UNION ALL
SELECT t.B AS x FROM test t
)
but it does not work because Oracle does not accept this syntax.
The two classic approaches:
have two unique constraints CREATE UNIQUE INDEX uidxA ON test A and CREATE UNIQUE INDEX uidxB ON test B does not work because then I could add 2 and 4 to column "A"
have a unique constraint of two columns CREATE UNIQUE INDEX uidxB ON test (A, B) because this check only existing pairs.
(Bonus question: it should be allowed that "A" and "B" of the same row can be equals)
SQL scripts for the example
CREATE TABLE test (id NUMBER (10) NOT NULL, a VARCHAR2(12), b VARCHAR2(12));
INSERT INTO test (id,a,b) VALUES(1, '1', '2');
INSERT INTO test (id,a,b) VALUES(2, '3', '4');
INSERT INTO test (id,a,b) VALUES(3, '4', 'x'); -> should fail
INSERT INTO test (id,a,b) VALUES(3, '5', 'x'); -> should work
#Tejash's answer gave me an idea to avoid locking or serialization. You can create an auxiliary table duet_index to produce the extended data set with all rows. Then a simple trigger will do the trick, including your bonus question.
For example:
create table duet_index (
n number,
constraint unique uq1 (n)
);
And then the trigger:
create or replace trigger test_trg
before insert on test
for each row
begin
insert into duet_index (n) values (:new.a);
if (:new.a <> :new.b) then
insert into duet_index (n) values (:new.b);
end if;
end;
Please consider I'm not proficient at writing Oracle triggers. The syntax can be wrong, but the idea should fly.
I've been working with Oracle for decades now and I don't recall having such a requirement. It makes me nervous about your data model.
What you want to do cannot be done with a single index. Trigger-based approaches are going to have trouble working correctly in all multi-user cases. A materialized-view approach seems promising.
My suggestion is to create a materialized view that refreshes on commit and that contains a concatenation (UNION ALL) of the column A and column B values.
Here is what I mean (see comments in code for more details):
create table test1 ( id number not null primary key, a number, b number );
insert into test1 values ( 1, 1, 2);
insert into test1 values ( 2, 3, 4);
commit;
-- Create a snapshot to allow us to create a REFRESH FAST ON COMMIT snapshot...
create snapshot log on test1 with primary key, rowid;
-- And create that snapshot... this will be updated when any changes to TEST1 are committed
create materialized view test1_concat
refresh fast on commit
as
select t1.rowid row_id, 1 as marker, t1.a concatenation from test1 t1
union all
select t2.rowid row_id, 2 as marker, t2.b concatenation from test1 t2
-- this next bit allows a = b in single rows (i.e., bonus question)
where t2.a != t2.b;
-- Now, enforce the constraint on our snapshot to prevent cross-column duplicates
create unique index test1_concat_u1 on test1_concat ( concatenation );
-- Test #1 -- column a may equal column b without error (bonus!)
insert into test1 values ( 3, 5, 5);
commit;
-- Test #2 uniqueness enforced
insert into test1 values ( 4, 6, 1);
-- (no error at this point)
commit;
> ORA-12008: error in materialized view refresh path ORA-00001: unique
> constraint (APPS.TEST1_CONCAT_U1) violated
Drawbacks
There is a scalability issue here. Oracle will synchronize on the commit. Every working solution to your problem will have this drawback, I believe
You do not get an error until the transaction tries to commit, at which point it is impossible to correct and recover the transaction. I believe you cannot solve this drawback in any solution without making drawback #1 much worse (i.e., without much more extensive and longer-lasting locks on your table).
I suggest fixing our data model, so the values are in rows rather than columns:
CREATE TABLE test (
id NUMBER (10) NOT NULL,
type varchar2(1) check (type in ('A', 'B'),
value varchar2(12),
unique (value),
unique (id, type)
);
The unique constraint is then easy.
Not possible using INDEX or CONSTRAINT. You need a trigger, something like this:
CREATE OR REPLACE TRIGGER TEST_TRG
BEFORE INSERT ON TEST
FOR EACH ROW
DECLARE
CNT NUMBER := 0;
BEGIN
SELECT COUNT(1) INTO CNT from TEST
WHERE A = :NEW.A OR B = :NEW.A OR A = :NEW.B OR B = :NEW.B;
IF CNT > 0 THEN
raise_application_error(-20111,'This value is not allowed');
END IF;
END;

sql query to display unavailable number?

i have some list of numbers with me, i want to check that numbers in a specified table available or not, if any number is not available in the table from the list of given numbers, then that unavailable number need to be display
I didnt sure that i understad your query correctly,
but it seems you mean somthing like this:
select *
from TABLE_NAME t
where t.COLUMN_NAME not in (1,2,3.... (#your list values#))
save the list of number first:
Create table list(id int);
insert into list(id)values(1);
insert into list(id)values(2);
insert into list(id)values(3);
insert into list(id)values(4);
Create sample table
Create table chck_list(id int,name varchar2(10));
insert into chck_list(id,name)values(1,'Micheal');
insert into chck_list(id,name)values(2,'John');
insert into chck_list(id,name)values(8,'Jack');
Query to check if there are id in list not showed in chck_list
select list.id
from list lst
where not exists(select 1 from chck_list chck where chck.id=lst.id)
Result
=========================
id
=========================
3
4

Insert into … values ( SELECT … FROM … ) in postgresql?

I am working with Postgresql database. I have one database which is - db1 and I have one table inside this database which is App1.
I need to make a select query against this App1 table which is in db1 and then whatever results I am getting back, I need to insert them in App2 table as it is which is in another database db2.
Below is my query which I am running against App1 table which is in db1 -
select col1, col2 from App1 limit 5
Now is there any way I can use Insert statement along with above SELECT statement which can insert into App2 table for me automatically which is in db2?
Something along this line -
Insert into … values ( SELECT … FROM … )
Is this possible to do in Postgresql as both the tables are in different database?
To do this between databases you must use the foreign data wrapper postgres_fdw or use dblink. See the documentation. PostgreSQL doesn't support cross-database SELECT.
Often, if you find yourself wanting to do this, you should be using separate schemas in a single database instead.
BTW, it's generally:
INSERT INTO ... SELECT ...
i.e. there's no subquery, no parentheses. That's because the VALUES clause is actually a standalone statement too:
INSERT INTO ... VALUES ...
observe:
regress=> VALUES (1,2), (2,3);
column1 | column2
---------+---------
1 | 2
2 | 3
(2 rows)

SQL Insert into 2 tables, passing the new PK from one table as the FK in the other

How can I achieve an insert query on 2 tables that will insert the primary key set from one table as a foreign key into the second table.
Here's a quick example of what I'm trying to do, but I'd like this to be one query, perhaps a join.
INSERT INTO Table1 (col1, col2) VALUES ( val1, val2 )
INSERT INTO Table2 (foreign_key_column) VALUES (parimary_key_from_table1_insert)
I'd like this to be one join query.
I've made some attempts but I can't get this to work correctly.
This is not possible to do with a single query.
The record in the PK table needs to be inserted before the new PK is known and can be used in the FK table, so at least two queries are required (though normally 3, as you need to retrieve the new PK value for use).
The exact syntax depends on the database being used, which you have not specified.
If you need this set of inserts to be atomic, use transactions.
Despite what others have answered, this absolutely is possible, although it takes 2 queries made consecutively with the same connection (to maintain the session state).
Here's the mysql solution (with executable test code below):
INSERT INTO Table1 (col1, col2) VALUES ( val1, val2 );
INSERT INTO Table2 (foreign_key_column) VALUES (LAST_INSERT_ID());
Note: These should be executed using a single connection.
Here's the test code:
create table tab1 (id int auto_increment primary key, note text);
create table tab2 (id int auto_increment primary key, tab2_id int references tab1, note text);
insert into tab1 values (null, 'row 1');
insert into tab2 values (null, LAST_INSERT_ID(), 'row 1');
select * from tab1;
select * from tab2;
mysql> select * from tab1;
+----+-------+
| id | note |
+----+-------+
| 1 | row 1 |
+----+-------+
1 row in set (0.00 sec)
mysql> select * from tab2;
+----+---------+-------+
| id | tab2_id | note |
+----+---------+-------+
| 1 | 1 | row 1 |
+----+---------+-------+
1 row in set (0.00 sec)
From your example, if the tuple (col1, col2) can be considered unique, then you could do:
INSERT INTO table1 (col1, col2) VALUES (val1, val2);
INSERT INTO table2 (foreign_key_column) VALUES (SELECT id FROM Table1 WHERE col1 = val1 AND col2 = val2);
There may be a few ways to accomplish this. Probably the most straight forward is to use a stored procedure that accepts as input all the values you need for both tables, then inserts to the first, retrieves the PK, and inserts to the second.
If your DB supports it, you can also tell the first INSERT to return a value:
INSERT INTO table1 ... RETURNING primary_key;
This at least saves the SELECT step that would otherwise be necessary. If you go with a stored procedure approach, you'll probably want to incorporate this into that stored procedure.
It could also possibly be done with a combination of views and triggers--if supported by your DB. This is probably far messier than it's worth, though. I believe this could be done in PostgreSQL, but I'd still advise against it. You'll need a view that contains all of the columns represented by both table1 and table2, then you need an ON INSERT DO INSTEAD trigger with three parts--the first part inserts to the new table, the second part retrieves the PK from the first table and updates the NEW result, and the third inserts to the second table. (Note: This view doesn't even have to reference the two literal tables, and would never be used for queries--it only has to contain columns with names/data types that match the real tables)
Of course all of these methods are just complicated ways of getting around the fact that you can't really do what you want with a single command.