Selecting Columns From a Record Variable (PostgreSQL 8.4) - function

I'm trying to select a column from a record variable in a function I'm calling from an Update rule and am getting the following error:
'could not identify column "name" in record data type'
The following is what I'm doing to produce the error:
From within an Update rule:
SELECT * INTO TEMPORARY TABLE TempTable FROM NEW;
SELECT MyFunction();
From within MyFunction()
DECLARE RecordVar Record;
SELECT * INTO STRICT RecordVar FROM TempTable;
EXECUTE 'UPDATE AnotherTable SET column = $1.name' USING RecordVar;
Note: I realise that there are easier ways to achieve what the above code is achieving but I've simplified the actual implementation to focus on the problem I'm having, which has opened up other possible solutions but I'd really like to get the above code working if possible.

I just figured it out. Rather than inserting the columns from NEW into the Temporary Table, I insert the NEW record as a single column into the Temporary Table and refer to it as RecordVar."NEW" inside my function. My rule and function now look like this:
From within an Update rule:
SELECT NEW AS "NEW" INTO TEMPORARY TABLE TempTable;
SELECT MyFuction();
From within MyFunction()
DECLARE RecordVar Record;
SELECT * INTO STRICT RecordVar FROM TempTable;
EXECUTE 'UPDATE AnotherTable SET column = $1.name' USING RecordVar."NEW";

The second part could work like this:
DO
$BODY$
DECLARE
RecordVar TempTable;
BEGIN
SELECT * INTO STRICT RecordVar FROM TempTable LIMIT 1;
EXECUTE 'UPDATE AnotherTable SET column = $1.name'
USING RecordVar;
END;
$BODY$
Note how I use the table name as type. PostgreSQL automatically creates a composite type for every table in the system.
A variable holds one row, the SELECT can return many rows. All but the first will be discarded. I added LIMIT 1 to clarify the effect. I doubt that is what you want.
You probably shouldn't have to use a temporary table in a rule to begin with. You may want to post your complete setup ...

Related

MySQL - Using SELECT for IF statement condition in stored procedure

I want to execute, in a stored procedure, a certain set of statements if, in table my_table there is exactly one row with value value in column column_name. I have tried the following, but I get a syntax error:
IF ((SELECT COUNT(*) FROM my_table WHERE column_name = value) = 1) THEN
BEGIN
END;
END IF;
For context: In my procedure I create a temporary table at some point, where I store a list of values. Then later on in the procedure, I want to check if a given value is present in that temporary table.
I think you might be better to structure it more like this
BEGIN
DECLARE myCOUNT INTEGER;
SELECT COUNT(*)
INTO myCount
FROM my_table
WHERE column_name=value;
IF (myCount = 1) THEN
-- do stuff
END IF;
END;
I'm not sure what you are trying to do, but I'll guess an "upsert" -- update a record if it exists, otherwise insert a new record.
In any case, if you are trying to ensure that name is unique in my_table, then this is not the right approach at all. Instead, declare a unique index/constraint so the database ensures the data integrity:
create unique index unq_my_table_name on my_table(name);
You can then use insert . . . on duplicate key update to modify the records in the database.

Trigger Preventing Record Insertion

I was trying to create trigger which can update value of column user_count of table user_details using value of u_count of table user_info.
CREATE TRIGGER `test`
AFTER INSERT ON `user_details` FOR EACH ROW
BEGIN
DECLARE default_user_count int(11);
SELECT u_count INTO #default_user_count FROM
user_info WHERE user_info.id= user_details.id_c;
IF user_details.user_count= 0
THEN UPDATE user_details SET
user_count = default_user_count
WHERE user_details.id_c = user_info.id;
END IF;
END
Trigger saved successfully but when i tried to insert value in both table it is preventing to insert record into user_details means no row inserted in 2 this table, if we delete trigger then its working.
Can anyone let me know wrong with this trigger?
THanks,
M.
It's not really clear what you're trying to accomplish, but it seems like it's something like what we have below.
There are numerous errors in and ambiguities in your trigger.
Confusion on variables -- DECLARE default_user_count INT(11); does not declare the user-defined variable #default_user_count. It declares the program variable default_user_count. The # prefix references an entirely different variable scope and namespace.
SELECT and UPDATE from the table which invoked the trigger doesn't usually make sense (SELECT) or is completely invalid (UPDATE).
With in a trigger, you are operating FOR EACH ROW -- that is, for each row included in the statement that invoked the trigger. Inside an INSERT trigger, the NEW values for the row are in a pseudo-table/pseudo-row accessible via the alias NEW. For UPDATE triggers, there are NEW and OLD row values, and for DELETE triggers, just OLD.
AFTER INSERT doesn't seem to make sense. I think you're looking for BEFORE INSERT -- that is, while processing an INSERT INTO ... query, before the newly-inserted row actually gets written into the table, modify its values accordingly. The resulting row contains the original values except where the trigger has modified them.
SELECT ... INTO a variable is a practice you should not get into the habit of, because it can bite you in a way a scalar subquery can't, by leaving a variable unexpectedly unaltered instead of setting it to NULL as would be expected. In this case, it would have made no difference, but it's still a caution worth mentioning... and in this case, I've eliminated that intermediate variable altogether, so the subquery is the only option.
If you are trying to set a value in this table using a value found in another table, all you need to do is SET NEW.column_name equal to the value you want used in the row instead of the value provided with the insert statement.
CREATE TRIGGER `test`
BEFORE INSERT ON `user_details` FOR EACH ROW
BEGIN
IF NEW.user_count = 0 /* maybe also >> */ OR NEW.user_count IS NULL /* << this */ THEN
SET NEW.user_count = (SELECT ui.u_count
FROM user_info ui
WHERE ui.id = NEW.id_c);
END IF;
END
Again, it's unclear how the two tables are connected based on the content of the original question, but this appears to do what you're trying to accomplish.

How to suppress mysql stored procedure output?

so if i have a stored procedure that contains selects how to suppress the results from these selects from appearing ?
for example if i have
create procedure xyz
begin
select * from table_name #I don't want this to be seen in the console.
do other stuff
end;
You can set the required params into variables like
SET #foo = (SELECT foo_coloumn FROM foo_table);
This will suppress the select result set.
The accepted answer didn't work for me since the resultset was multirow. I did figure out a solution, though:
CREATE TEMPORARY TABLE silence_output SELECT * FROM foo;
DROP TEMPORARY TABLE silence_output;
This avoids any multirow/multicolumn issues that might be encountered by dumping the resultset into a table that is then trashed. Hope this helps someone else. You can name the temporary table anything.
I was struggling to find a way to suppress multirow output in a stored procedure while at the same time locking all applicable rows with "FOR UPDATE".
First attempt was "SELECT ... INTO" with a "LIMIT" clause but that would only lock 1 row.
Using GROUP_CONCAT solved it for me:
SELECT GROUP_CONCAT(column) INTO #variable
FROM table
WHERE column = value
FOR UPDATE
Doing that in a transaction locked all affected rows, prevented an insert, and suppressed output.

mysql cursor fetch without declaring variables (possible?)

I have a cursor which is declared as so:
DECLARE staging_cur CURSOR FOR
SELECT
col1, col2, ......
FROM crawl_db.staging_listing
WHERE is_deleted = FALSE;
I then fetch each row, perform some checks and then insert the row into another (production) database
OPEN staging_cur;
the_loop: LOOP
FETCH staging_cur
INTO col1_val, col2_val,.....;
-- perform some checks and some optional inserts
-- for example, if city with given name is not found in production DB, insert it
-- insert into production db
END LOOP the_loop;
I realize I need to declare a variable (col1_val, col2_val ...) for each corresponding column of table staging_listing (col1, col2....). The problem is that this table contains 90-100 columns and declaring all variables is really cumbersome
It seems there should be a better way than this. Is there some way in which we can access the column of the cursor's current row without having to declare separate variables to hold the column values?
If you need to insert rows into another table, then a better way is to use INSERT...SELECT statement. Try to avoid using cursors.
INSERT ... SELECT Syntax.

Retrieve generated ID in MS SQL 2008

I'm converting a ColdFusion Project from Oracle 11 to MS SQL 2008. I used SSMA to convert the DB including triggers, procedures and functions. Sequences were mapped to IDENTITY columns.
I planned on using INSERT-Statements like
INSERT INTO mytable (col1, col2)
OUTPUT INSERTED.my_id
values('val1', 'val2')
This throws an error since the table has a trigger defined, that AFTER INSERT writes some of the INSERTED data to another table to keep a history of the data.
Microsoft writes:
If the OUTPUT clause is specified without also specifying the INTO
keyword, the target of the DML operation cannot have any enabled
trigger defined on it for the given DML action. For example, if the
OUTPUT clause is defined in an UPDATE statement, the target table
cannot have any enabled UPDATE triggers.
http://msdn.microsoft.com/en-us/library/ms177564.aspx
I'm now wondering what is the best practice fo firstly retrieve the generated id and secondly to "backup" the INSERTED data in a second table.
Is this a good approach for the INSERT? It works because the INSERTED value is not simply returned but written INTO a temporary variable. It works in my tests as Microsoft describes without throwing an error regarding the trigger.
<cfquery>
DECLARE #tab table(id int);
INSERT INTO mytable (col1, col2)
OUTPUT INSERTED.my_id INTO #tab
values('val1', 'val2');
SELECT id FROM #tab;
</cfquery>
Should I use the OUTPUT clause at all? When I have to write multiple clauses in one cfquery-block, shouldn't I better use SELECT SCOPE_DENTITY() ?
Thanks and best,
Bernhard
I think this is what you want to do:
<cfquery name="qryInsert" datasource="db" RESULT="qryResults">
INSERT INTO mytable (col1, col2)
</cfquery>
<cfset id = qryResults.IDENTITYCOL>
This seems to work - the row gets inserted, the instead of trigger returns the result, the after trigger doesn't interfere, and the after trigger logs to the table as expected:
CREATE TABLE dbo.x1(ID INT IDENTITY(1,1), x SYSNAME);
CREATE TABLE dbo.log_after(ID INT, x SYSNAME,
dt DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP);
GO
CREATE TRIGGER dbo.x1_after
ON dbo.x1
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
INSERT dbo.log_after(x) SELECT x FROM inserted;
END
GO
CREATE TRIGGER dbo.x1_before
ON dbo.x1
INSTEAD OF INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #tab TABLE(id INT);
INSERT dbo.x1(x)
OUTPUT inserted.ID INTO #tab
SELECT x FROM inserted;
SELECT id FROM #tab;
END
GO
Now, if you write this in your cfquery, you should get a row back in output. I'm not CF-savvy so I'm not sure if it has to see some kind of select to know that it will be returning a result set (but you can try it in Management Studio to confirm I am not pulling your leg):
INSERT dbo.x1(x) SELECT N'foo';
Now you should just move your after insert logic to this trigger as well.
Be aware that right now you will get multiple rows back for (which is slightly different from the single result you would get from SCOPE_IDENTITY()). This is a good thing, I just wanted to point it out.
I have to admit that's the first time I've seen someone use a merged approach like that instead of simply using the built-in PK retrieval and splitting it into separate database requests (example).