Combining Queries into one and iterate for multiple unique identifiers - sql

I am trying to see if there is a way to combine CTE query and the Update query into one query. Also, I need to iterate the following query for multiple "AttributeId", when I try to put multiple AttributeID's in "in clause" it does not give me the result I want. Is there a better way of writing this query and instead of repeating the same query over and over again for different attribute ID - is the a way to write it like we write For Each loop? Thank you very much for your help!

Yes you can update a table from a CTE result.
I've already done that in the past and I've built this SQL Fiddle as an example if you want to see it live.
So, you need to use the UPDATE statement combined with the FROM clause and specify the "join" condition in the WHERE, like this:
;WITH CTE
AS
(
-- Fill the data you want to update
SELECT personID, COUNT(1) AS count
FROM person_movies
GROUP BY personID
)
-- Update statement. You can use SELECT, UPDATE, INSERT or DELETE
UPDATE persons
-- Specify the columns you want to update
SET moviecount = CTE.count
-- Specify the source, in this case is from the CTE
FROM CTE
-- How can we "link" the results in both tables? Specify it here
WHERE persons.personID = CTE.personID

Related

Postgres: select for update using CTE

I always had a query in the form of:
UPDATE
users
SET
col = 1
WHERE
user_id IN (
SELECT
user_id
FROM
users
WHERE
...
LIMIT 1
FOR UPDATE
);
And I was pretty sure that it generates a lock on the affected row until the update is done.
Now I wrote the same query using CTE and doing
WITH query AS (
select
user_id
FROM
users
WHERE
...
LIMIT 1
FOR UPDATE
)
UPDATE
users
SET
col = 1
WHERE
user_id IN (
SELECT
user_id
FROM
query
);
I’m actually having some doubts that it is applying a row lock because of the results I get, but I couldn’t find anything documented about this.
Can someone make it clear? Thanks
Edit:
I managed to find this:
If specific tables are named in FOR UPDATE or FOR SHARE, then only rows coming from those tables are locked; any other tables used in the SELECT are simply read as usual. A FOR UPDATE or FOR SHARE clause without a table list affects all tables used in the statement. If FOR UPDATE or FOR SHARE is applied to a view or sub-query, it affects all tables used in the view or sub-query. However, FOR UPDATE/FOR SHARE do not apply to WITH queries referenced by the primary query. If you want row locking to occur within a WITH query, specify FOR UPDATE or FOR SHARE within the WITH query.
https://www.postgresql.org/docs/9.0/sql-select.html#SQL-FOR-UPDATE-SHARE
So I guess it should work only if the for update is in the with and not in the query that is using the with?

is there an alternative to query with DELETE snowflake SQL statement with CTE?

On snowflake, is there an alternative to query with DELETE SQL statement with CTE? seems it is not possible.
with t as (
select * from "SNOWFLAKE_SAMPLE_DATA"."TPCDS_SF100TCL"."CALL_CENTER"
), p as (select t.CC_REC_END_DATE, t.CC_CALL_CENTER_ID , t.CC_REC_START_DATE from t where 1=1 AND t.CC_REC_START_DATE > '2000-01-01')
delete from p
For example: if we use a select, we got some results.
but if I use a delete. It shows a syntax error
The problem with this thinking is that SELECT returns some values, that might, or might not be, matching individual records in your table. In principle, they might return even combinations of values from multiple tables, multiple rows, or no rows at all even. What would you want DELETE to do then?
So, for DELETE, you need to specify which rows in the table you're operating on are deleted. You can do it with a simple WHERE clause, or with a USING clause.
If you want CTEs with DELETE, the closest would be to use USING, put a subquery there, and join its result with the table. In many cases that's a very useful approach, but it is causing a join, which has a performance impact. Example:
delete from CALL_CENTER t
using (
select cc_call_center_sk from CALL_CENTER
where 1=1 AND t.CC_REC_START_DATE > '2000-01-01'
) AS sub
where sub.cc_call_center_sk = t.cc_call_center_sk.
But again, for your query it doesn't make much sense, and what #cddt wrote is probably your best bet.
The expected outcome from the question is not clear, so this is my best interpretation of it.
It seems like you want to delete records from the table SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.CALL_CENTER where the field CC_REC_START_DATE is greater than 2000-01-01. If that's what you want to achieve, then you don't need a CTE at all.
DELETE FROM SNOWFLAKE_SAMPLE_DATA.TPCDS_SF100TCL.CALL_CENTER t
WHERE t.CC_REC_START_DATE > '2000-01-01'

Unable to get a stable set of rows in the source tables

I am facing this issue, can someone help to validate this merge statement?
MERGE INTO WC_FNHLDNG_D T1
USING (SELECT distinct ROW_WID, CONTACT_WID
FROM W_ASSET_D
WHERE X_TYPE_CD='Fin Account')T2
ON (T1.ASSET_WID=T2.ROW_WID)
WHEN MATCHED THEN UPDATE
SET T1.CONTACT_WID=T2.CONTACT_WID;
Acquire locks on WC_FNHLDNG_D and W_ASSET_D before attempting the MERGE operation.
The rule of merge is that there must be no more than one row in the USING subquery which matches a row in the target table.
Your subquery is this:
SELECT distinct ROW_WID, CONTACT_WID
FROM W_ASSET_D
So if there is more than one different CONTACT_WID for a given ROW_WID it will return multiple rows for the ROW_WID. The DISTINCT clause won't help, because the CONTACT_WID are different.
The fact that your statement hurls ORA-30926 suggests this is the state of your data. Oracle doesn't know which W_ASSET_D.CONTACT_WID is the right one to merge into WC_FNHLDNG_D so it gives up. The solution to rewrite the USING subquery so it returns only one CONTACT_WID per ROW_WID.
You should have some additional business rules you can add in a WHERE clause. But as a last resort you can use an aggregating function, e.g.
USING (SELECT ROW_WID, max( CONTACT_WID) as CONTACT_WID
FROM W_ASSET_D
WHERE X_TYPE_CD='Fin Account'
group by ROW_WID
)T2
This is better than choosing a random CONTACT_WID but not by much :)

Need to do a bulk update to a subset of a table in SQL?

I need to bulk update a list of records resulting from the query:
select *
from Subscribers S with (nolock)
where S.COUNTRY_CD is not null and S.REGION is null
So, running the above query gives me ONLY the records that I need to update.
Then, as I alluded to, I need to perform an update to ONLY update these records. I'm not a SQL whiz by any means. How can I basically iterate through the result set I get above and perform an update on each of these records?
Update has quite flexible syntax in SQL Server. When you have a SELECT of the form
SELECT Cols
FROM T
JOIN ...
WHERE Condition
and want to make it into an UPDATE you mechanically transform it to this:
UPDATE T
SET ...
FROM T
JOIN ...
WHERE Condition
Note, that I just pasted the last three lines without modification. You should now be able to adapt your query to be a set-based update.

Is CTE's stored (like a table) or do you create it each time you need it? (SQL Server 2005)

Never worked with MS SQL Server before and the CTE's is new to me. But it's exactly what I need in the project I just started.
Have a table that looks like
TABLE group (
'id' int,
'parentId' int,
'groupName' varchar(255)
)
So I have a dynamic page and depending on a get parameter, "Group" I want all that's below this group returned. I'm pretty sure I can manage to write the CTE for it as well as use it.
But do I create it each time I wish to use it? (basically connect to DB, run the setup for the CTE, then use a query running on this).
Or are they stored in the database once they're created and ready to be used whenever?
If they are stored, is there any command similar to sp_tables, etc to show it /them?
Cause if they're saved there probably already exist a query that does this.
A CTE is just a different way of formin a SQL expression.
Where one comment states that it "is not a view", it is fairly similar to an inline-view. The difference being that you can reference it several times in the same query, or have recursive declarations...
WITH cte AS (...) SELECT * FROM cte
is similar to
SELECT * FROM (...)
They are not, however, persisted. You can't even do the following...
WITH
cte AS
(
...
)
SELECT * INTO #temp FROM cte
SELECT * FROM cte
The SELECT INTO will work, but by the time of the second select, the CTE is out of scope...
CTE are evaluated/created/consumed at run time. They're available only to the next single SQL statement that follows after the WITH ... SELECT.
You could insert their resultset into a table variable or temp table to have their results available for a longer duration.
You may be thinking of a View.
From MSDN:
A common table expression (CTE) can be
thought of as a temporary result set
that is defined within the execution
scope of a single SELECT, INSERT,
UPDATE, DELETE, or CREATE VIEW
statement. A CTE is similar to a
derived table in that it is not stored
as an object and lasts only for the
duration of the query. Unlike a
derived table, a CTE can be
self-referencing and can be referenced
multiple times in the same query.