Retrieve value of a column after update? - sql-server

I update a counter (no autoincrement ... not my database ...) with this FDQuery SQL:
UPDATE CountersTables
SET Cnter = Cnter + 1
OUTPUT Inserted.Cnter
WHERE TableName = 'TableName'
I execute FDQuery.ExecSQL and it works: 'Cnter' is incremented.
I need to retrieve the new 'Counter' value but the subsequent command
newvalue := FDQuery.FieldByName('Cnter').AsInteger
Fails with error:
... EDatabaseError ... 'CountersTables: Field 'Cnter' not found.
What is the way to get that value?

TFDQuery.ExecSQL() is meant for queries that don't return records. But you are asking your query to return a record. So use TFDQuery.Open() instead, eg:
FDQuery.SQL.Text :=
'UPDATE CountersTables' +
' SET Cnter = Cnter + 1' +
' OUTPUT Inserted.Cnter' +
' WHERE TableName = :TableName';
FDQuery.ParamByName('TableName').AsString := 'TableName';
FDQuery.Open;
try
NewValue := FDQuery.FieldByName('Cnter').AsInteger;
finally
FDQuery.Close;
end;
If the database you are connected to does not support OUTPUT, UPDATE OUTPUT into a variable shows some alternative ways you can save the updated counter into a local SQL variable/table that you can then SELECT from.

You have also the RETURNING Unified support Ok, doc only shows INSERT SQL but UPDATE works too.
And I should use a substitution variable for tablename

Related

Assign result from stored procedure to a variable

I have created a stored procedure that returns a create table sql statement; I want to be able to now call that procedure and assign the result to a variable like:
set create_table_statement = call sp_create_stage_table(target_db, table_name);
snowflake will not let me do this, so is there a way I can.
Context
We have just been handed over our new MDP which is built on AWS-S3, DBT & Snowflake, next week we go into production but we have 200+ tables and snowlpipes to code out. I wanted to semi automate this by generating the create table statements based off the tables metadata and then calling the results from that to create the tables. At the moment we're having to run the SQL, copy+paste the results in and then run that, which is fine in dev/pre-production mode when it's a handful of tables. but with just 2 of us it will be a lot of work to get all those tables and pipes created.
so I've found a work around, by creating a second procedure and calling the first one as a se=ql string to get the results as a string - then calling that string as a sql statement. like:
create or replace procedure sp_create_stage_table("db_name" string, "table_name" string)
returns string
language javascript
as
$$
var sql_string = "call sp_get_create_table_statement('" + db_name + "','" + table_name + "');";
var get_sql_query = snowflake.createStatement({sqlText: sql_string});
var get_result_set = get_sql_query.execute();
get_result_set.next();
var get_query_value = get_result_set.getColumnValue(1);
sql_string = get_query_value.toString();
try {
var main_sql_query = snowflake.createStatement({sqlText: sql_string});
main_sql_query.execute();
return "Stage Table " + table_name + " Successfully created in " + db_name + " database."
}
catch (err){
return "an error occured! \n error_code: " + err.code + "\n error_state: " + err.state + "\n error_message: " + err.message;
}
$$;
It is possible to assign scalar result of stored procedure to session variable. Instead:
SET var = CALL sp();
The pattern is:
SET var = (SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID())));
Sample:
CREATE OR REPLACE PROCEDURE TEST()
RETURNS VARCHAR
LANGUAGE SQL
AS
BEGIN
RETURN 'Result from stored procedrue';
END;
CALL TEST();
SET variable = (SELECT * FROM TABLE(RESULT_SCAN(LAST_QUERY_ID())));
SELECT $variable;
-- Result from stored procedrue

Modify the metadata of a database

This is simple, i have a database that contains many tables and what i want to do is to add a default value to all the fields that represent a boolean (a char(1 byte)). So is there a way to (using a function) write some logic that uses the meta-data of the database and its tables to add that default value without iterating manually on each field in each table ?
Hope this is clear guys :)
Use the block given below for doing your task
DECLARE
LV_SQL VARCHAR2(4000);
CURSOR C_GET_COLUMNS IS
SELECT TABLE_NAME,COLUMN_NAME,NULLABLE,DATA_LENGTH,DATA_TYPE
FROM USER_TAB_COLUMNS
WHERE DATA_TYPE = 'CHAR'
AND DATA_LENGTH = 1;
BEGIN
FOR I IN C_GET_COLUMNS LOOP
LV_SQL := 'ALTER TABLE '||I.TABLE_NAME||' MODIFY '||I.COLUMN_NAME||' '||I.DATA_TYPE||'('||I.DATA_LENGTH||') DEFAULT '||CHR(39)||'Y'||CHR(39);
EXECUTE IMMEDIATE LV_SQL;
LV_SQL := 'UPDATE '||I.TABLE_NAME||' SET '||I.COLUMN_NAME||' = '||CHR(39)||'Y'||CHR(39)||' WHERE '||I.COLUMN_NAME||' IS NULL';
DBMS_OUTPUT.PUT_LINE(LV_SQL);
EXECUTE IMMEDIATE LV_SQL;
END LOOP;
END;

For loop cursor in teradata

In my Teradata Stored Procedure, I want to have a for loop cursor against a dynamic sql.
Below is the code snippet
SET get_exclude_condition = '';
SET colum_id = 'SELECT MIN (parent_criteria_id) ,MAX (parent_criteria_id) FROM arc_mdm_tbls.intnl_mtch_criteria WHERE act_ind = 1 AND criteria_typ = ''Exclude'' AND mtch_technique_id ='||mtch_technique_id||';' ;
PREPARE input_stmt FROM colum_id;
OPEN flex_cursor;
FETCH flex_cursor INTO parent_criteria_id_min , parent_criteria_id_max ;
CLOSE flex_cursor;
SET get_exclude_condition = '';
WHILE (parent_criteria_id_min <= parent_criteria_id_max)
DO
SET get_exclude_condition = get_exclude_condition || '( ';
SET for_loop_stmt = 'SELECT criteria FROM arc_mdm_tbls.intnl_mtch_criteria WHERE act_ind = 1 AND mtch_technique_id ='||mtch_technique_id||' AND criteria_typ= ''Exclude'' AND parent_criteria_id ='||parent_criteria_id_min||';';
FOR for_loop_rule AS c_cursor_rule CURSOR FOR
for_loop_stmt
DO
Can I declare a for loop cursor like this ?
Or do I need to have something like this only ?
FOR for_loop_rule AS c_cursor_rule CURSOR FOR
SELECT rule_id
FROM arc_stage_tbls.assmt_scoring_rules
WHERE rule_typ = :v_RuleType
ORDER BY rule_id
DO
I mean can I first frame the dynamic sql and then have a for loop cursor on top of that or with the cursor declaration only I need to have a static sql query ?
Please clarify.
While you haven't posted everything that the stored procedure is trying to accomplish, it does appear that what you are asking can be accomplished using SET based logic and not looping through a cursor. If you need to parameterize the 'mtch_technique_id' you can use a Teradata macro which will allow you to maintain a SET based approach.
Here is the SQL for creating a macro that returns a result set based on my interpretation of what your snippet of the stored procedure is trying to accomplish:
REPLACE MACRO {MyDB}.Intnl_Mtch_Criteria(mtch_technique_id INTEGER) AS
(
SELECT criteria
FROM arc_mdm_tbls.intnl_mtch_criteria
WHERE act_ind = 1
AND (much_technique_id, criteria_typ) IN
(SELECT MIN((parent_criteria_id), MAX (parent_criteria_id)
FROM arc_mdm_tbls.intnl_mtch_criteria
WHERE act_ind = 1
AND criteria_typ = 'Exclude'
AND mtch_technique_id = :mtch_technique_id;
);

Update table column using table in another SQL Server database while using a spatial function

I would like to update a table column using a spatial function with another database table. This is what I've come up with....
UPDATE FirstDatabase.dbo.track_logs
SET county = t2.CountyName
FROM OtherDatabase.dbo.tblCounty AS t2
WHERE t2.cty_geog.STIntersects(
GEOGRAPHY::STPointFromText('Point(' + FirstDatabase.dbo.track_logs.lng + ' ' +
FirstDatabase.dbo.track_logs.lat + ')', 26915)
)
but I get this error...
An expression of non-boolean type specified in a context where a condition is expected, near ')'.
Almost there. This is one of those silly things in TSQL. You need a = 1.
That's the answer to your question, but I would also be tempted to use POINT(lat, lon, SRID) to address your comment.
UPDATE FirstDatabase.dbo.track_logs
SET county = t2.CountyName
FROM OtherDatabase.dbo.tblCounty AS t2
WHERE t2.cty_geog.STIntersects(geography::Point(FirstDatabase.dbo.track_logs.lat,
FirstDatabase.dbo.track_logs.lng,
26915)) = 1
In a language like C#, you could write something like:
bool val = true;
if (val)
// do stuff
But in TSQL, you have to write the equivalent to:
bool val = true;
if (val == true)
// do stuff
This isn't specific to SQL Spatial, of course, you'd also have to specify WHERE bitColumnName = 1 or, as your example illustrates, WHERE bitReturningFunction(args) = 1.

Cannot insert duplicate key in object (GetReparentedValue / hierarchyid)

Using examples I found on the web I have created a function which reparents children using the GetReparentedValue.
However when I have ran the code I get the following error: Cannot insert duplicate key in object.
I understand why (because I am trying to reparent the children and the new parent already has children so I need to know the MAX path (hierarchyid) of the child within the new parent structure, but I don't understand how I'm actually going to do that.
path 0x58
oldPath 0x
new path 0x68
SqlCommand command = new SqlCommand("UPDATE Structure SET " +
"Path = " + path + ".GetReparentedValue" +
"(" +
oldPath + ", " + newPath +
")" +
"ParentID = #id " +
"WHERE Path = " + path, _connection);
I have to do this when adding a child so I thought it would need to add this somewhere to the query above but I dont know where path + ".GetDescendant(" + lastChildPath + ", NULL)
Database Table
StructureID int Unchecked
Path hierarchyid Unchecked
PathLevel ([Path].[GetLevel]()) Checked
Description nvarchar(50) Checked
ParentID int Checked
ParentPath ([Path].[GetAncestor]((1))) Checked
Anyone have any suggestion?
Thanks in advance for any help :-)
Clare
There are a couple of changes you can make to get this to work. First, you don't need the oldPath that represents the parent of the node that you want to move. In the .GetReparentedValue function, you put the hierarchyid of the node that is moving, which is the value in path.
The second change is to add another SELECT statement to apply your GetDescendant function. Here's a sample script that you can try in SQL Server Management Studio (SSMS), or alter to incorporate into your SQLCommand calls. The first few lines (variable declarations are assignments) are only for running in SSMS. You would transfer the last SELECT and the UPDATE statements to the calling code.
DECLARE #Path hierarchyid
DECLARE #oldPath hierarchyid
DECLARE #newPath hierarchyid
SELECT #Path=0x58, #oldPath=0x, #newPath=0x68
SELECT #newPath = #newPath.GetDescendant(MAX(Path), NULL)
FROM Structure
WHERE path.GetAncestor(1)=#newPath;
UPDATE Structure
SET Path = Path.GetReparentedValue(#Path, #newPath)
WHERE Path = #Path;
Your UPDATE statement and this revision will only re-parent a single node. It will not automatically move the children of the moving node. Children of the moving-node will be orphaned.
If you need to move the selected node and all descendants of the node, you can use the following variation of the previous statements.
DECLARE #Path hierarchyid
DECLARE #oldPath hierarchyid
DECLARE #newPath hierarchyid
SELECT #Path=0x58, #oldPath=0x, #newPath=0x68
SELECT #newPath = #newPath.GetDescendant(MAX(Path), NULL)
FROM Structure
WHERE Path.GetAncestor(1) = #newPath ;
UPDATE Structure
SET Path = Path.GetReparentedValue(#Path, #newPath)
WHERE Path.IsDescendantOf(#Path) = 1;
Actually, the only change from the first script to this script is in the very last line. The Path.IsDescendantOf(#Path) = 1 test is true for all descendants of #Path, including #Path. The hierarchical relationships will be maintained after the update.
This is another example of moving a subtree and all of it's children. It is essentially the same as the accepted answer. This is taken from the Docs:
CREATE PROCEDURE MoveOrg(#oldMgr nvarchar(256), #newMgr nvarchar(256) )
AS
BEGIN
DECLARE #nold hierarchyid, #nnew hierarchyid
SELECT #nold = OrgNode FROM HumanResources.EmployeeDemo WHERE LoginID = #oldMgr ;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
SELECT #nnew = OrgNode FROM HumanResources.EmployeeDemo WHERE LoginID = #newMgr ;
SELECT #nnew = #nnew.GetDescendant(max(OrgNode), NULL)
FROM HumanResources.EmployeeDemo WHERE OrgNode.GetAncestor(1)=#nnew ;
UPDATE HumanResources.EmployeeDemo
SET OrgNode = OrgNode.GetReparentedValue(#nold, #nnew)
WHERE OrgNode.IsDescendantOf(#nold) = 1 ;
COMMIT TRANSACTION
END ;
GO

Resources