How to use triggers for cross table updating - sql-server-2008

I have three columns of data (A, B, C) in two tables (table_a, table_b) where I want to use a trigger that will update a column in table_a with concatenated column values from table_a and table_b anytime a record is updated in table_a.
CREATE TABLE 'table_a'
(
'id' BIGINT(50) NOT NULL,
'A' VARCHAR(5) NULL,
'B' VARCHAR(5) NULL,
'C' VARCHAR(5) NULL,
PRIMARYKEY ('id')
)
CREATE TABLE 'table_b'
(
'id' BIGINT(50) NOT NULL,
'A' VARCHAR(5) NULL,
'B' VARCHAR(5) NULL,
'C' VARCHAR(5) NULL,
PRIMARYKEY ('id')
)
INSERT INTO table_a (A, B, C) VALUES (1, 2, 3)
INSERT INTO table_b (A, B, C) VALUES (4, 5, 6)
Without a trigger, I would use this statement to update table_a
UPDATE Table_a
SET table_a.A = COALESCE(table_a.B, 0) + ’,’ + COALESCE(table_b.A, 0)
FROM table_a
INNER JOIN table_b ON table_a.id = table_b.id
WHERE table_a.id = 1
After UPDATE
table_a:
| A | B | C |
|------+----+----+
| 2, 4 | 2 | 3 |
This is my attempt at the trigger
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TRIGGER update_table_a
ON table_a
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
DECLARE #id as BIGINT
SELECT #id = id FROM updated
UPDATE table_a
SET table_a.A = COALESCE(table_a.B, 0) + ‘,’ + COALESCE(table_b.A, 0)
FROM Table_a
INNER JOIN table_b ON table_a.id = table_b.id
WHERE table_a.id = #id
END
GO
My problem is that the value in table_a.A reverts back to its original value when I attempt to change the value in that column with this trigger in place.

There is no pseudo table called updated - in the case of an UPDATE trigger, you have Deleted with the old values (before the update happened), and Inserted with the new values after the update.
You need to use this properly set-based code to handle this update - you need to make use of the Inserted pseudo table which contains all the new values of the rows that have been updated (in table_A).
CREATE TRIGGER update_table_a
ON table_a
AFTER UPDATE
AS
BEGIN
UPDATE table_a
SET A = COALESCE(i.B, 0) + ‘,’ + COALESCE(table_b.A, 0)
FROM Table_a
INNER JOIN table_b ON table_a.id = table_b.id
INNER JOIN Inserted i ON table_a.id = i.id
END
GO

Related

Create view to get values from multiple tables

I thought it is easier to edit this post and give exact example what I meant by that post after all.
Main idea is that I need to get values from different tables.
Basically main idea is to select all values from Table B and Table C, but
selecting only values from Table C which are not present in Table B (but at the same time I need to
left join Table C to get text column value).
Table B and Table C has similar structure. They both have ref_num (ID) and text value.
Also Table B holds Table C ref_num, because when Table C entity is modified ("modifiable_column"),
then record is saved into Table B, but "default" value text column is taken from Table C.
It's something like.
Let's say we have default rules (Table C - always same values forever), then we have custom rules (Table B). Table D holds version of each rule with current as end_date IS NULL. Default values have default "modifiable_column" as 'N' as mentioned before. Now, let's say I take one rule from Table C and change "modifiable_column" to 'Y'. Then new row is created into Table B (with ref_num, table_c_ref_num, text = NULL). It means that this rule is now custom for this particular TabelA ref_num, at the same time new row is inserted into Table D (holding new row ref_num as table_b_ref_num and new "modifiable_column" value).
Now, when I want to select custom rules, default rules and versions (end_date IS NULL). I have to join Table B, Table C and Table D. But as Table C has always same rules, I only need to join it to get the text value. And I have to make sure I won't select duplicates. Meaning if Table C has 10 default rules, now one was modified and custom rules (Table B) has 1 rule. Then I have to so said select 1 from Table B and 9 from Table C, but at the same time I need to join Table C text value for this custom rule.
I have following tables as below:
create table TableA (
ref_num INT
);
create table TableB (
ref_num INT,
text VARCHAR(100),
table_c_ref_num INT,
table_a_ref_num INT
);
create table TableC (
ref_num INT,
text VARCHAR(100)
);
create table TableD (
ref_num INT,
table_b_ref_num INT,
modfifable_column VARCHAR(1),
start_date date,
end_date date
);
Inserting initial values as below:
insert into TableA (ref_num) values (1);
insert into TableC (ref_num, text) values
(1, "Text 1"),
(2, "Text 2"),
(3, "Text 3");
insert into TableB (ref_num, text, table_c_ref_num, table_a_ref_num) values
(1, NULL, 2, 1);
insert into TableD (ref_num, table_b_ref_num, modfifable_column, start_date, end_date) values
(1, 1, 'Y', now(), NULL);
Now I have created this select statement to get wanted behaviour:
SELECT * FROM (
SELECT
tb.ref_num AS ref_num,
tb.table_a_ref_num AS table_a_ref_num,
coalesce(tc.text, tb.text),
coalesce(tc.ref_num, tb.table_c_ref_num) AS table_c_ref_num,
coalesce(td.modfifable_column, 'N') AS modfifable_column
FROM TableB tb
LEFT JOIN TableD td on td.table_b_ref_num = tb.ref_num AND td.end_date IS NULL
LEFT JOIN TableC tc on tc.ref_num = tb.table_c_ref_num
WHERE tb.table_a_ref_num = 1
) as cust
UNION ALL
SELECT * FROM (
SELECT
NULL AS ref_num,
NULL AS table_a_ref_num,
tc2.text AS text,
tc2.ref_num AS table_c_ref_num,
'N' AS modfifable_column
FROM TableC tc2
WHERE tc2.ref_num NOT IN (
SELECT
tb2.table_c_ref_num
FROM TableB tb2
LEFT JOIN TableD td on td.table_b_ref_num = tb2.ref_num AND td.end_date IS NULL
LEFT JOIN TableC tc on tc.ref_num = tb2.table_c_ref_num
WHERE tb2.table_a_ref_num = 1
)
) as def;
I know that I can turn those two inner SELECT statements into views and then combine them with UNION ALL for example. My biggest concern here is that I have to "hard code" table_a_ref_num = 1 into two different places.
Because I have to use TableA ref_num in order to get custom values from TableB and default values from TableC. Because at the end TableA ref_num is like “this specific” entity custom rules and default rules.
My question is: Is there a way to wrap my big SELECT clause into one view, where I could use this TableA ref_num value to get results as in my example.
I don't fully understand your tables and pseudo code, but based on this limited understanding, my suggestion would be that you start with a query similar to this on:
select 'ta',ta.*,'tb',tb.*,'tc_via_b',tc_via_b.*,'tc_via_a',tc_via_a.*,'td',td.*
from table_a ta
left join table_b tb on tb.table_a_ref_num = ta.ref_num
left join table_c tc_via_b on tc_via_b.ref_num = tb.table_c_ref_num
left join table_c tc_via_a on tc_via_a.ref_num = ta.ref_num
left join table_d td on td.table_b_ref_num = tb.ref_num AND td.end_date IS NULL;
This way you will see all lines you want as a first step. In a second step, you should be able to use NVL and CASE to get the data you want. HTH

Updating column based on relation to another table

I have a table_1 that looks something like this
NameId SelectedName
Null A
Null C
Null F
NameId is a FOREIGN KEY that REFERENCES table_2 (Id)
And a table_2 that looks like this
Id Name
1 A
2 B
3 C
4 D
5 E
6 F
I want to populate NameId with all of the Id numbers associated with the same Name, so for example, the final result would be:
NameId SelectedName
1 A
3 C
6 F
So far, all I have is:
SELECT Id, `Name` FROM table_2 WHERE `Name` IN (SELECT `SelectedName` FROM table_1);
Which I then tried to follow up with:
UPDATE table_1 SET NameId = Id WHERE SelectedName = `Name`;
Which doesn't work. Not sure how to go about doing this or how to use previously selected columns and relations to get what I want done here...
UPDATE table1
INNER JOIN table2
ON table1.SelectedName= table2.Name
SET table1.NameId = table2.ID
UPDATE Table1 a INNER JOIN Table2 b ON a.SelectedName = b.Name SET a.NameId = b.ID
First Update the table_1.NameId values with table_2.Id
UPDATE table_1 INNER JOIN table_2 ON table_1.SelectedName= table_2.Name
SET table_1.NameId= table_2.Id
and retrieve the values from table_1
SELECT NameId, SelectedName
FROM table_1
WHERE SelectedName IN (SELECT Name FROM table_2);
This way will update relation table column when parent has update.
DROP TRIGGER `table_2_au`;
DELIMITER ;;
CREATE TRIGGER `table_2_au` AFTER UPDATE ON `table_2` FOR EACH ROW
UPDATE `table_1` T1
INNER JOIN `table_2` T2
ON T1.`SelectedName` = T2.`Name`
SET T1.`NameId` = T2.`Id`;;
DELIMITER ;

MySQL Update not updating anything, but Select returns results

This is truly boggling myself and my manager. Have two tables:
TableA (
id VARCHAR(15) NOT NULL,
category VARCHAR(35) DEFAULT '',
foo VARCHAR(3) DEFAULT '',
INDEX (category),
INDEX (foo)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
TableB (
id VARCHAR(15),
INDEX (id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
And I run the select that I need to update on:
SELECT A.foo, A.id, B.id
FROM TableA A
LEFT JOIN TableB B
ON A.id = B.id
WHERE A.category = ''
AND A.foo NOT IN ('bar', '')
AND B.id IS NULL;
Returns ~20,000 results, returns exactly what I expect and want it to return.
But
UPDATE TableA A
LEFT JOIN TableB B
ON A.id = B.id
SET A.category='known'
WHERE A.category=''
AND A.foo NOT IN ('bar', '')
AND B.id IS NULL;
updates nothing, and
UPDATE
TableA A,
TableB B
SET A.category='known'
WHERE A.category=''
AND A.foo NOT IN ('bar', '')
AND B.id IS NULL;
updates nothing either. No matter what we try we can't get these category fields to update.
Well, no wonder you're mind-boggled, so am I. Might it be something to do with NOT IN? Try AND A.foo <> 'bar' AND A.foo <> ''
Adding indexes on the join columns is a must in any case and a FK would not hurt.
Amazed that this was the solution, NOT IN must act like a select from table subquery?? You learn something new every day, or can if you try :)
Your query is perfect.
Although you're seeing ~20,000 result in your select statement, but if there is nothing to update, you'll see 0 row affected.
Let's see these queries to understand this scenario.
-- create employee table
CREATE TABLE employee (id INT, emp_name VARCHAR(20), dept_id INT);
-- insert records to employee
INSERT INTO employee(id, emp_name, dept_id)
VALUES (1, 'ram', 1), (2, 'shyam', 1), (3, 'sita', 2)
-- create department
CREATE TABLE department(id INT, dept_name VARCHAR(20))
-- insert recrods to department
INSERT INTO department(id, dept_name)
VALUES (1, 'java'), (2, '.net')
-- update
-- 1st execution it will show 2 rows affected.
UPDATE employee AS e
JOIN department AS d ON e.dept_id = d.id
SET e.dept_id = 1
,d.dept_name = 'dot net'
WHERE d.id = 2;
-- update (same statement as above)
-- 2nd execution (as we've already update just now will show 0 rows affected.
UPDATE employee AS e
JOIN department AS d ON e.dept_id = d.id
SET e.dept_id = 1
,d.dept_name = 'dot net'
WHERE d.id = 2;
I don't think you can update in a join.....I would do an update after one or 2 query's and base it on the results etc

Update a column in a table

I need to update a column in a table which is of int data type but the values from other table i used for updating is nvarchar.
wheather this is achieved if so please let me know.
It could be something like this:
update tablename
set t.column = convert(int, t2.column)
from tablename t
inner join secondtablename t2 on t.column = t2.column
where ISNUMERIC(t2.column) = 1
If all you data in the nvarchar column are numbers, then, you should be able to do:
update ATable
set intColumn = cast(o.chardata as int)
from ATable a
join OtherTable o on a.tableid=o.tableid
now, you can also put in logic to handle non numeric data with an ISNUMERIC() constraint.
I see aF beat me to the punch.
CREATE TABLE #t
(
ID int IDENTITY(1,1),
Column1 int
)
CREATE TABLE #t1
(
ID int IDENTITY(1,1),
Column2 Varchar(50)
)
INSERT INTO #t(Column1)VALUES(1)
INSERT INTO #t(Column1)VALUES(2)
INSERT into #t1(Column2)values('Alpha Numeric')
INSERT into #t1(Column2)values('12')
UPDATE t
SET t.Column1 = t1.Column2
FROM #t t
INNER join #t1 t1 on t.ID = t1.ID
Where ISNUMERIC(t1.Column2) = 1
select * FROM #t
DROP TABLE #t
DROP TABLE #t1

How can I count all the records in the both tables in a 1:n relationship in 1 query?

I'm guessing this a fairly easy question, but I've run into this problem a number of times and I can't find a solution either through searching (maybe I don't know what to search for) or trial and error.
I have the following table structure and data:
CREATE TABLE `table_a` (
`id` int(11) NOT NULL auto_increment,
`table_b_id` int(11) NOT NULL,
`field` varchar(10) NOT NULL,
PRIMARY KEY (`id`),
KEY `table_b_id` (`table_b_id`)
);
INSERT INTO `table_a` VALUES(1, 1, 'test 1');
INSERT INTO `table_a` VALUES(2, 1, 'test 2');
INSERT INTO `table_a` VALUES(3, 0, 'test 3');
CREATE TABLE `table_b` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(10) NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO `table_b` VALUES(1, 'value 1');
INSERT INTO `table_b` VALUES(2, 'value 2');
As you can see, id 2 in table_b is not used in table_a and id 3 in table_a has a value of 0 for table_b_id. What I want to do is retrieve a count of the number of times each value in b is used in table_a, including 2 from table_b and count of all the values not in table_b.
I have come up with the following query:
SELECT b.name, COUNT(a.id) AS number
FROM table_a AS a
LEFT JOIN table_b AS b ON (b.id = a.table_b_id)
GROUP BY a.table_b_id
But it obviously only returns the following:
name number
-----------------
null 1
value 1 2
How can I get the following, with only SQL:
name number
-----------------
null 1
value 1 2
value 2 0
I am using MySQL. I'm guessing the answer is simple.
Edit: Is there no other way other than a union'd query?
SELECT b.name, SUM(case when a.id is null then 0 else 1 end) AS number
FROM table_a AS a
RIGHT JOIN table_b AS b ON (b.id = a.table_b_id)
GROUP BY b.id
If you want to get the null row, that is special. I believe you'll have to UNION it.
SELECT b.name, SUM(case when a.id is null then 0 else 1 end) AS number
FROM table_a AS a
RIGHT JOIN table_b AS b ON (b.id = a.table_b_id)
GROUP BY b.id
UNION
select null, count(1) from table_a where table_b_id not in ( select id from table_b )
What you want to do here is called FULL OUTER JOIN.
As for now, MySQL lacks this feature, that means you'll need to emulate is with a UNION (which is not too bad as you still do it in a single query).
SELECT name, SUM(IF(a.id IS NULL, 0, 1))
FROM table_b b
LEFT JOIN
table_a a
ON a.table_b_id = b.id
GROUP BY
b.id
UNION ALL
SELECT name, COUNT(*)
FROM table_a a
LEFT JOIN
table_b b
ON b.id = a.table_b_id
WHERE b.id IS NULL
GROUP BY
b.id
This nested query would work with mysql 5
SELECT b.name, (SELECT COUNT( * ) FROM table_a AS a WHERE a.table_b_id = b.id) AS amount FROM table_b AS b
There is a lengthy discussion of various ways to solve this problem on this post by Xaprb. He shows how to avoid using a UNION, although the alternative mechanism requires an extra "mutex" table and he suggests it's slower than using a UNION.