CTE: SELECT statement to read rows made by an INSERT Statement - sql

I have a CTE that first inserts rows, and then reads the table with the inserted rows. Right now, the read on the table does not take into account the inserted rows.
The simplest example could be like this:
The Table:
CREATE TABLE mytable (column1 text, column2 text);
The query:
WITH insert_first AS (
INSERT INTO mytable (column1, column2)
VALUES ('value1', 'value2')
RETURNING *
), select_after AS (
SELECT * FROM mytable
LEFT JOIN insert_first ON insert_first.column1 = mytable.column1
) SELECT * FROM select_after
Here, select_after will be empty.
I thought by doing a LEFT JOIN on insert_first, I would hint to SQL to wait for the insert. But, it does not seem to do this.
Is there a way I could make a query that runs over mytable, which sees the inserts made from insert_first?
Here's a playground too: https://www.db-fiddle.com/f/4jyoMCicNSZpjMt4jFYoz5/6796

As Adrian mentioned in the comments, this is not possible:
From docs WITH: The primary query and the WITH queries are all (notionally) executed at the same time. This implies that the effects of a data-modifying statement in WITH cannot be seen from other parts of the query, other than by reading its RETURNING output. If two such data-modifying statements attempt to modify the same row, the results are unspecified.

Related

MERGE INTO Performance

I have a table contains tat contains {service_id, service_name,region_name}
As input my procedure gets service_id , i_svc_region list of key,value pairs, which has {service_name, region}.
Have to insert into the table if the record does not exists already. I know it is a very simple query.. But does the below queries make any difference in performance?
which one is better and why?
MERGE INTO SERVICE_REGION_MAP table1
USING
(SELECT i_svc_region(i).key as service_name,i_enabled_regions(i).value as region
FROM dual) table2
ON (table1.service_id =i_service_id and table1.region=table2.region)
WHEN NOT MATCHED THEN
INSERT (service_id,service_name ,region) VALUES (i_service_id ,table2.service_name,table2.region);
i_service_id - is passed as it is.
MERGE INTO SERVICE_REGION_MAP table1
USING
(SELECT i_service_id as service_id, i_svc_region(i).key as service_name,i_enabled_regions(i).value as region
FROM dual) table2
ON (table1.service_id =table2.service_id and table1.region=table2.region)
WHEN NOT MATCHED THEN
INSERT (service_id,service_name ,region) VALUES (table2.service_id,table2.service_name,table2.region);
i_service_id is considered as column in table.
Does this really make any difference?
You should be using the FORALL statement. It will result in much faster performance than any looping we could write. Check out the documenation, starting with https://docs.oracle.com/database/121/LNPLS/forall_statement.htm#LNPLS01321
As #Brian Leach suggests the FORALL will give you a single round trip to SQL engine for all of the elements (i's) in your table. This can give between 10 and 100 times improvement depending on table size and many other things beyond me.
Also you are only using the INSERT capability of MERGE so a time honoured INSERT statement should make life easier/faster for the database. MERGE has more bells and whistles which can slow it down.
So try something like:
FORALL i IN 1..i_svc_region(i).COUNT
INSERT INTO SERVICE_REGION_MAP table1
(service_id, service_name, region)
SELECT
i_service_id AS service_id,
i_svc_region(i).KEY AS service_name,
i_enabled_regions(i).VALUE AS region
FROM DUAL table2
WHERE NOT EXISTS
( SELECT *
FROM SERVICE_REGION_MAP table1
WHERE table1.service_id=table2.service_id AND table1.region=table2.region
);

Insert data into multiple tables from one select statement in sql

I need to insert data into two different tables via a select statement.This select statement is calling an inline TVF.
What I have so far is :
INSERT INTO #Temp2 (RowNumber, ValFromUser, ColumnName, ValFromFunc, FuncWeight, percentage)
SELECT
RowNumber, #hospitalname, 'hospitalname',
PercentMatch, #constVal, PercentMatch * #constVal
FROM
dbo.Matchhospitalname (#hospitalname)
But there are certain columns that need to be supplied to a permanent table dbo.Cache.
Above mentioned query is called multiple times in the procedure.
Insert into dbo.Cache(StringSearched, ColName, RowId, PercentMatch)
select
ValFromUser, ColumnName, RowNumber, Max(ValFromFunc) as Percentage
from
#Temp2
group by
ValFromUser, ColumnName, RowNumber
Adding data into dbo.Cache separately as above would make all the previously added values to be added as many times as this statement is executed which is of course not desirable.
May be if it is not possible at all to add data to two tables via one select, we can do something like adding only those rows that were added in last insert statement only ?
Can I get some directions on this, please?
Edit : As suggested, I tried using OUTPUT INTO this way but Group by seems to be at the wrong place.The grouped rowsare to be inserted only in dbo.Cache and not in #Temp2
How do I solve this ?
INSERT INTO #Temp2 (RowNumber,ValFromUser,ColumnName,ValFromFunc,FuncWeight,percentage)OUTPUT
INSERTED.ValFromUser,
INSERTED.ColumnName,
INSERTED.RowNumber,
MAX(INSERTED.ValFromFunc)
INTO dbo.CACHE
(StringSearched, ColName, RowId, PercentMatch)
Group By Inserted.ValFromUser, Inserted.ColumnName, Inserted.RowNumber
SELECT RowNumber,#firstname,'firstname',PercentMatch,#constVal,PercentMatch * #constVal FROM dbo.MatchFirstName(#firstname)
You can do it via an output clause or more typically you can put a trigger on a table. In other words you can create an after insert trigger on temp table '#temp2'. I have never seen a trigger on a temp table but its possible. You will have to recreate the trigger every time the temp table is recreated. Remember that #temp2 will only exist (and be visible) in the session that it is created in.

SQL. When I trying to do something like "INSERT INTO Table VALUES(x1,x2,x3) - can the x1 x2 x3 be sql queries, like SELECT <...>

I want to do something like this:
QSqlQuery q;
q.prepare("insert into Norm values(select from Disc id_disc WHERE name_disc=?, select from Spec code_spec WHERE name_spec=?,?");
q.addBindValue(MainModel->data(MainModel->index(MainModel->rowCount()-1, 1)).toString());
q.addBindValue(ui->comboBox->currentText());
q.addBindValue(MainModel->data(MainModel->index(MainModel->rowCount()-1, 2)).toString());
q.exec();
But it's not working. Surely for someone obviously where is the error and maybe he tells me how to do it right.
First of all your you have done spelling mistake. Its "INSERT" not "INCERT"
And yes we can insert SELECT query inside INSERT query.
eg:
INSERT INTO table2
(column_name(s))
SELECT column_name(s)
FROM table1;
INSERT ... SELECT ... is used when you want to insert multiple records, or when most values to be inserted come from the same record.
If you want to insert one record with values coming from several tables, you can use subqueries like you tried to do, but you have to use the correct syntax:
scalar subqueries must be written inside parentheses, and you must write the SELECT correctly as SELECT value FROM table:
INSERT INTO Norm
VALUES ((SELECT id_disc FROM Disc WHERE name_disc = ?),
(SELECT code_spec FROM Spec WHERE name_spec = ?),
?)
If you want data from two tables, you must first write a query which return pretended data - using JOIN, UNION, subqueries, ...
Then, just do
INSERT INTO target_table SELECT ...

SQL Insert/Update Issue

I am trying to update one table from another, im able to update fine as long as the customer record exists, but there are some entries that dont.
To solve this i've tried running the following insert
SELECT *
INTO SalBudgetCust
FROM SalBudgetCust_temp
WHERE NOT EXISTS (
SELECT Customer
FROM SalBudgetCust
WHERE Customer = SalBudgetCust_temp.Customer
)
but im prompted with
There is already an object named 'SalBudgetCust' in the database.
Im stuck at this point... could anyone offer a little guideance?
SELECT INTO implicitly creates the table you name. You should instead use INSERT INTO ... SELECT * FROM ..., so that the existing table is used.
It should be INSERT INTO instead of SELECT * INTO ... like
INSERT INTO SalBudgetCust SELECT * FROM SalBudgetCust_temp
WHERE NOT EXISTS
(
SELECT Customer FROM SalBudgetCust WHERE Customer = SalBudgetCust_temp.Customer
)
The general syntax to insert data of one table into another is :
INSERT INTO new_table
SELECT * FROM old_table
WHERE some_condition;
Where, new_table is the table where you want to insert data, old_table is table from where you are fetching data and some_condition is the expression / condition based upon which you want to fetch data from old table.
You may use other clauses like order by, group by, and even sub queries after where clause.
May refer this SQL INSERT INTO and it's subsequent pages.

IF-Statement in SQLite: update or insert?

I Can't run this query with SQLite
if 0<(select COUNT(*) from Repetition where (Word='behnam' and Topic='mine'))
begin
update Repetition set Counts=1+ (select Counts from Repetition where (Word='behnam' and Topic='mine'))
end
else
begin
insert Repetition(Word,Topic,Counts)values('behnam','mine',1)
end
It says "Syntax error near IF"
How can I solve the problem
SQLite does not have an IF statement (see the list of supported queries)
Insetad, check out out ERIC B's suggestion on another thread. You're effectively looking at doing an UPSERT (UPdate if the record exists, INSERT if not). Eric B. has a good example of how to do this in SQLite syntax utilizing the "INSERT OR REPLACE" functionality in SQLite. Basically, you'd do something like:
INSERT OR REPLACE INTO Repetition (Word, Topic, Counts)
VALUES ( 'behnam', 'mine',
coalesce((select Counts + 1 from Repetition
where Word = 'behnam', AND Topic = 'mine)
,1)
)
Another approach is to INSERT ... SELECT ... WHERE ... EXISTS [or not] (SELECT ...);
I do this sort of thing all the time, and I use jklemmack's suggestion as well. And I do it for other purposes too, such as doing JOINs in UPDATEs (which SQLite3 does not support).
For example:
CREATE TABLE t(id INTEGER PRIMARY KEY, c1 TEXT NOT NULL UNIQUE, c2 TEXT);
CREATE TABLE r(c1 TEXT NOT NULL UNIQUE, c2 TEXT);
INSERT OR REPLACE INTO t (id, c1, c2)
SELECT t.id, coalesce(r.c1, t.c1), coalesce(r.c2, t.c2)
FROM r LEFT OUTER JOIN t ON r.c1 = t.c1
WHERE r.c2 = #param;
The WHERE there has the condition that you'd have in your IF. The JOIN in the SELECT provides the JOIN that SQLite3 doesn't support in UPDATE. The INSERT OR REPLACE and the use of t.id (which can be NULL if the row doesn't exist in t) together provide the THEN and ELSE bodies.
You can apply this over and over. If you'd have three statements (that cannot somehow be merged into one) in the THEN part of the IF you'd need to have three statements with the IF condition in their WHEREs.
This is called an UPSERT (i.e. UPdate or inSERT). It has its forms in almost every type of database. Look at this question for the SQLite version: SQLite - UPSERT *not* INSERT or REPLACE
One way that I've found is based on SQL WHERE clause true/false statement:
SELECT * FROM SOME_TABLE
WHERE
(
SELECT SINGLE_COLUMN_NAME FROM SOME_OTHER_TABLE
WHERE
SOME_COLUMN = 'some value' and
SOME_OTHER_COLUMN = 'some other value'
)
This actually means execute some QUERIES if some other QUERY returns 'any' result.