I have a WITH statement that collects the data I want. What I want to do is to be able to do a SELECT from different parts of my WITH and INSERT that result into a destination table.
Here is a simplified version of my long query:
WITH Active AS (
--SELECT 1
),
Inactive AS (
--SELECT 2
),
Churn AS (
--SELECT 3
)
--Drop destination table if exists
IF OBJECT_ID('DestinationTable', 'u') IS NOT NULL DROP TABLE DestinationTable;
SELECT Active.Name, Inactive.Name,Churn.Id
INTO DestinationTable
FROM Active a
JOIN Inactive i ON a.Id = i.Id
JOIN Churn c ON a.Id = c.Id;
But Sql Server does not allow an IF statement directly after a WITH. I do not want to move the IF before my WITH because then it will be likely that my destination table be empty for a long time.
Simple version of my question:
How can I write a SELECT from a WITH statement into another table?
Because it is compulsory to use with statement result follow by with statement.
Any other statement except with result not allowed.
You can write your drop statement above the with statement
Just restructure it like this:
--Drop destination table if exists
IF OBJECT_ID('DestinationTable', 'u') IS NOT NULL DROP TABLE DestinationTable
;WITH Active AS (
--SELECT 1
),
Inactive AS (
--SELECT 2
),
Churn AS (
--SELECT 3
)
SELECT Active.Name, Inactive.Name,Churn.Id
INTO DestinationTable
FROM Active a
JOIN Inactive i ON a.Id = i.Id
JOIN Churn c ON a.Id = c.Id;
The scope of the CTE means it needs to be used in the statement immediately after it's declaration.
Otherwise you need some other staging area to handle the load before you replace the destination table.
Related
I want to create a trigger that will fill up my sales history base after firing in the ORDER table.
I am creating a specific order in regular data base and after that this order automatically goes to sales_history database.
Below part works properly.
When I create a new order in regular database my sales_history database is growing with new ID_ORDERS, hooray! :)
ALTER TRIGGER [dbo].[InsertTrig]
ON [dbo].[ORDER]
AFTER INSERT
AS
IF EXISTS (SELECT *
FROM inserted i
WHERE i.ID_TYPE = 1) -- specific order type
BEGIN
INSERT INTO id.dbo.sales_history (id_order)
SELECT i.ID_ORDER FROM inserted i
END
The problem arises when I want join another table. The trigger stops working
ALTER TRIGGER [dbo].[InsertTrig]
ON [dbo].[ORDER]
AFTER INSERT
AS
IF EXISTS (SELECT *
FROM inserted i
WHERE i.ID_TYPE = 1) -- specific order type
BEGIN
INSERT INTO id.dbo.sales_history (id_order, id_item)
SELECT
inserted.ID_ORDER, ORDER_DETAILS.ID_ITEM
FROM
inserted
INNER JOIN
ORDER_DETAILS ON ORDER_DETAILS.ID_ORDER = inserted.ID_ORDER
END
I also tried this way, and still nothing :(
ALTER TRIGGER [dbo].[InsertTrig]
ON [dbo].[ORDER]
AFTER INSERT
AS
IF EXISTS (SELECT *
FROM inserted i
WHERE i.ID_TYPE = 1) -- specific order type
BEGIN
DECLARE #xyz AS numeric(18, 0)
SET #xyz = (SELECT inserted.ID_ORDER FROM inserted)
INSERT INTO id.dbo.sales_history (id_order, id_item)
SELECT
ORDER.ID_ORDER, ORDER_DETAILS.ID_ITEM
FROM
ORDER
INNER JOIN
ORDER_DETAILS ON ORDER_DETAILS.ID_ORDER = ORDER.ID_ORDER
WHERE
ORDER.ID_ORDER = #xyz
END
I want to create a trigger that will automatically fill up my sales history base after firing in ORDER table.
Your trigger is on the Order table, meaning SQL Server fires it after you insert records into the Order table. At which point, the relevant records in the Order_Details table couldn't have been inserted yet, because they have a foreign key to the Order table.
This is why an inner join between your inserted table and the Order_details table returns 0 rows.
If you want your sales_history from the order_details table, you have to populate it after you insert the records to the order_details table.
CREATE OR ALTER TRIGGER [dbo].[OrderDetails_AfterInsert]
ON [dbo].[ORDER_DETAILS]
AFTER INSERT
AS
INSERT INTO id.dbo.sales_history (id_order, id_item)
SELECT
inserted.ID_ORDER, inserted.ID_ITEM
FROM
inserted
INNER JOIN
[ORDER] ON [ORDER].ID_ORDER = inserted.ID_ORDER
WHERE [ORDER].ID_TYPE = 1 -- specific order type
As a side note: InsertTrig is bad name. Note the name of the trigger in my answer - it tells you exactly what this trigger is for, and on what table.
I have a CTE I am using to pull some data from two tables then stick in an intermediate table called cte_list, something like
with cte_list as (
select pl.col_val from prune_list pl join employees.employee emp on pl.col_val::uuid = emp.id
where pl.col_nm = 'employee_ref_id' limit 100
)
Then, I am doing an insert to move records from the cte_list to another archive table (if they don't exist) called employee_arch_test
insert into employees.employee_arch_test (
select * from employees.employee where id in (select col_val::uuid from cte_list)
and not exists (select 1 from employees.employee_arch_test where employees.employee_arch_test.id=employees.employee.id)
);
This seems to work fine. The problem is when I add another statement after, to do some deletions from the main employee table using this aforementioned cte_list - the cte_list apparently no longer exists?
SQL Error [42P01]: ERROR: relation "cte_list" does not exist
the actual delete query:
delete from employees.employee where id in (select col_val::uuid from cte_list);
Can the cte_list CTE table only be used once or something? I'm running these statements in a LOOP and I need to run the exact same calls for about 2 or 3 other tables but hit a sticking point here.
A CTE only exists for the duration of the statement of which it's a part. I gather you have an INSERT statement with the CTE preceding it:
with cte_list
as (select pl.col_val
from prune_list pl
join employees.employee emp
on pl.col_val::uuid = emp.id
where pl.col_nm = 'employee_ref_id'
limit 100
)
insert into employees.employee_arch_test
(select *
from employees.employee
where id in (select col_val::uuid from cte_list)
and not exists (select 1
from employees.employee_arch_test
where employees.employee_arch_test.id = employees.employee.id)
);
The CTE is part of the INSERT statement - it is not a separate statement by itself. It only exists for the duration of the INSERT statement.
If you need something which lasts longer your options are:
Add the same CTE to each of your following statements. Note that because data may be changing in your database each invocation of the CTE may return different data.
Create a view which performs the same operations as the CTE, then use the view in place of the CTE. Note that because data may be changing in your database each invocation of the view may return different data.
Create a temporary table to hold the data from your CTE query, then use the temporary table in place of the CTE. This has the advantage of providing a consistent set of data to all operations.
I'm trying to move a column from one table to another (here's the post for that),
However this runs as a task, and may run after it has already completed so I need a clause that prevents the logic from running again. I thought I could achieve this with an IF:
IF EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'Table_A')
AND name = 'internalID'
)
BEGIN
UPDATE Table_B
SET b.internalID = a.internal_ID
FROM Table_B b INNER JOIN
Table_A a
ON a.id = b.FK_toTableA;
ALTER TABLE Table_A DROP COLUMN internalID;
END
However, I get an error at
SET b.internalID = a.internal_ID
The error is:
Invalid column name 'internalID'.
But Only If a.internalID doesn't exist anymore.
I had to use EXEC sp_executesql:
IF EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'Table_A')
AND name = 'internalID'
)
BEGIN
EXEC sp_executesql N'UPDATE Table_B
SET b.internalID = a.internal_ID
FROM Table_B b INNER JOIN
Table_A a
ON a.id = b.FK_toTableA';
ALTER TABLE Table_A DROP COLUMN internalID;
END
I guess because SQL Server complies the whole thing - even that stuff bypassed via conditional logic - before running the script.
Here's a good article about what sql_executesql does, compared to EXEC, but it basically boils down to more injection prevention.
Curious to know one simple SQL concept in SQL Server.
I have two tables, based on certain IF condition , I would like to update and insert data from different tables.I'm using below query for this in side a stored procedure.
IF( SELECT COUNT(colname) FROM [dbo].[Table_BKP] WHERE colname LIKE ('%Dear(DEC''D)444-0292/555-4528C%'))> 0
BEGIN
;WITH Dear AS
(
SELECT ID,colname,SUBSTRING(colname,PATINDEX('%(DEC''D)%',colname),LEN(colname)-1) AS colname_4SUPPL,LEFT(colname,PATINDEX('%(DEC''D)%',colname)-1) AS colname_4MAIN
FROM [dbo].[Table_BKP] WHERE RTRIM(LTRIM(colname)) LIKE ('Dear(DEC''D)444-0292/555-4528C')
) SELECT * INTO Dear_ FROM Dear
UPDATE A SET A.colname = B.colname_4MAIN FROM Table AS A INNER JOIN
Dear_ AS B ON A.ID = B.ID
INSERT INTO Table_SUPPL(ID,colnameMSC,[SOURCE],[TARGET])
SELECT ID,replace(colname_4SUPPL,'(DEC''D)',''),'colname','colnameMSC'
FROM Dear_
END
update statement is working fine and data is getting updated into the required table.However, the data is not getting inserted into the required table.
But, when I changed the sequence as below, it worked perfectly.
IF( SELECT COUNT(colname) FROM [dbo].[Table_BKP] WHERE colname LIKE ('%Dear(DEC''D)444-0292/555-4528C%'))> 0
BEGIN
;WITH Dear AS
(
SELECT ID,colname,SUBSTRING(colname,PATINDEX('%(DEC''D)%',colname),LEN(colname)-1) AS colname_4SUPPL,LEFT(colname,PATINDEX('%(DEC''D)%',colname)-1) AS colname_4MAIN
FROM [dbo].[Table_BKP] WHERE RTRIM(LTRIM(colname)) LIKE ('Dear(DEC''D)444-0292/555-4528C')
) SELECT * INTO Dear_ FROM Dear
INSERT INTO Table_SUPPL(ID,colnameMSC,[SOURCE],[TARGET]) SELECT
ID,replace(colname_4SUPPL,'(DEC''D)',''),'colname','colnameMSC' FROM
Dear_
UPDATE A SET A.colname = B.colname_4MAIN FROM Table AS A INNER JOIN
Dear_ AS B ON A.ID = B.ID
END
Why, the formar quesry is not working as expected? And, the later one [just after changing sequesnce (1st INSERT then Update)]; Its working perfectly...How SQL Server manages these sequence? Could anyone please clarify this simple doubt? Thanks.
I have the following Query and i need the query to fetch data from SomeTable based on the filter criteria present in the Someothertable. If there is nothing present in SomeOtherTable Query should return me all the data present in SomeTable
SQL SERVER 2005
SomeOtherTable does not have any indexes or any constraint all fields are char(50)
The Following Query work fine for my requirements but it causes performance problems when i have lots of parameters.
Due to some requirement of Client, We have to keep all the Where clause data in SomeOtherTable. depending on subid data will be joined with one of the columns in SomeTable.
For example the Query can can be
SELECT
*
FROM
SomeTable
WHERE
1=1
AND
(
SomeTable.ID in (SELECT DISTINCT ID FROM SomeOtherTable WHERE Name = 'ABC' and subid = 'EF')
OR
0=(SELECT Count(1) FROM SomeOtherTable WHERE spName = 'ABC' and subid = 'EF')
)
AND
(
SomeTable.date =(SELECT date FROM SomeOtherTable WHERE Name = 'ABC' and subid = 'Date')
OR
0=(SELECT Count(1) FROM SomeOtherTable WHERE spName = 'ABC' and subid = 'Date')
)
EDIT----------------------------------------------
I think i might have to explain my problem in detail:
We have developed an ASP.net application that is used to invoke parametrize crystal reports, parameters to the crystal reports are not passed using the default crystal reports method.
In ASP.net application we have created wizards which are used to pass the parameters to the Reports, These parameters are not directly consumed by the crystal report but are consumed by the Query embedded inside the crystal report or the Stored procedure used in the Crystal report.
This is achieved using a table (SomeOtherTable) which holds parameter data as long as report is running after which the data is deleted, as such we can assume that SomeOtherTable has max 2 to 3 rows at any given point of time.
So if we look at the above query initial part of the Query can be assumed as the Report Query and the where clause is used to get the user input from the SomeOtherTable table.
So i don't think it will be useful to create indexes etc (May be i am wrong).
SomeOtherTable does not have any
indexes or any constraint all fields
are char(50)
Well, there's your problem. There's nothing you can do to a query like this which will improve its performance if you create it like this.
You need a proper primary or other candidate key designated on all of your tables. That is to say, you need at least ONE unique index on the table. You can do this by designating one or more fields as the PK, or you can add a UNIQUE constraint or index.
You need to define your fields properly. Does the field store integers? Well then, an INT field may just be a better bet than a CHAR(50).
You can't "optimize" a query that is based on an unsound schema.
Try:
SELECT
*
FROM
SomeTable
LEFT JOIN SomeOtherTable ON SomeTable.ID=SomeOtherTable.ID AND Name = 'ABC'
WHERE
1=1
AND
(
SomeOtherTable.ID IS NOT NULL
OR
0=(SELECT Count(1) FROM SomeOtherTable WHERE spName = 'ABC')
)
also put 'with (nolock)' after each table name to improve performance
The following might speed you up
SELECT *
FROM SomeTable
WHERE
SomeTable.ID in
(SELECT DISTINCT ID FROM SomeOtherTable Where Name = 'ABC')
UNION
SELECT *
FROM SomeTable
Where
NOT EXISTS (Select spName From SomeOtherTable Where spName = 'ABC')
The UNION will effectivly split this into two simpler queries which can be optiomised separately (depends very much on DBMS, table size etc whether this will actually improve performance -- but its always worth a try).
The "EXISTS" key word is more efficient than the "SELECT COUNT(1)" as it will return true as soon as the first row is encountered.
Or check if the value exists in db first
And you can remove the distinct keyword in your query, it is useless here.
if EXISTS (Select spName From SomeOtherTable Where spName = 'ABC')
begin
SELECT *
FROM SomeTable
WHERE
SomeTable.ID in
(SELECT ID FROM SomeOtherTable Where Name = 'ABC')
end
else
begin
SELECT *
FROM SomeTable
end
Aloha
Try
select t.* from SomeTable t
left outer join SomeOtherTable o
on t.id = o.id
where (not exists (select id from SomeOtherTable where spname = 'adbc')
OR spname = 'adbc')
-Edoode
change all your select statements in the where part to inner jons.
the OR conditions should be union all-ed.
also make sure your indexing is ok.
sometimes it pays to have an intermediate table for temp results to which you can join to.
It seems to me that there is no need for the "1=1 AND" in your query. 1=1 will always evaluate to be true, leaving the software to evaluate the next part... why not just skip the 1=1 and evaluate the juicy part?
I am going to stick to my original Query.