How to update in SQL based on different where clause - sql-server-2008

I have a table which has a structure as:
ID DBInstance DBName Tag
1 | INS1 | master | NULL
2 | INS1 | tempdb | NULL
4 | INS2 | master | NULL
5 | INS2 | tempdb | NULL
I want to update the tag in this table as based on the condition as:
1) Update tag as "a" only for DBInstance as "INS1" and DBName as "master"
2) Update tag as "b" only for DBInstance as "INS2" and DBName as "tempdb".
And I want to update both of these two only in a single statement, not in two different update queries. How can I do so?
A query somewhat like this:
UPDATE tbl_test
SET tag = 'a' where DBInstance in ('INS1') and DBName IN ('master'),
tag = 'b' where DBInstance in ('INS2') and DBName IN ('tempdb')
But obviously, this query is wrong, so how can I do so?

You can use the case when then like this:
UPDATE tbl_test
SET tag = case
when DBInstance = 'INS1' and DBName = 'master'
then 'a'
when DBInstance = 'INS2' and DBName = 'tempdb'
then 'b'
else NULL -- or may be tag, or default value which you want.
end

Related

Conditional table updates

Consider the following table.
myTable
+----+-----------+------------------------------------+
| Id | responseA | responseB |
+----+-----------+------------------------------------+
| 1 | | {"foo":"bar","lvl2":{"key":"val"}} |
+----+-----------+------------------------------------+
where:
Id, INT (11) PRIMARY
responseA, TEXT utf8_unicode_ci
responseB, TEXT utf8_unicode_ci
Let's say that I want to conditionally update the table with some outside data. The conditions are:
• if there's nothing in responseA, populate it with the outside data, otherwise
• if there is something in responseA, leave it as it is, and populate responseB with the outside data
I was pretty much convinced that I could just do this to get what I want:
UPDATE myTable
SET
responseA = IF(TRIM(responseA) = '','foo',TRIM(responseA)),
responseB = IF(TRIM(responseA) != '','foo',TRIM(responseB))
WHERE Id = 1
However, this updates both responseA and responseB to the same value - foo, making the table:
myTable
+----+-----------+-----------+
| Id | responseA | responseB |
+----+-----------+-----------+
| 1 | foo | foo |
+----+-----------+-----------+
I was expecting my table to look like this after the update:
myTable
+----+-----------+------------------------------------+
| Id | responseA | responseB |
+----+-----------+------------------------------------+
| 1 | foo | {"foo":"bar","lvl2":{"key":"val"}} |
+----+-----------+------------------------------------+
What am I misunderstanding, and how can I achieve this conditional update? Do the updates happen sequentially? If so, I guess that would explain why both of the fields are updated.
UPDATE TABLE
SET responseA = CASE WHEN responseA IS NULL
THEN #data
ELSE responseA
END,
responseB = CASE WHEN responseA IS NULL
THEN responseB
ELSE #data
END
;
here your changed query
UPDATE myTable
SET
responseB = IF(TRIM(responseA) != '','foo',TRIM(responseB)),
responseA = IF(TRIM(responseA) = '','foo',TRIM(responseA))
WHERE Id = 1
It seems the value of responseA is changed before the IF() for responseB is evaluated.
One possible solution is to do a simple UPDATE:
UPDATE mytable SET responseA = ? WHERE id = 1
Then adjust the columns in a trigger, where you have access to both the original and the new value of the columns:
CREATE TRIGGER t BEFORE UPDATE ON mytable
FOR EACH ROW BEGIN
IF TRIM(OLD.responseA) != '' THEN
SET NEW.responseB = NEW.responseA;
SET NEW.responseA = OLD.responseA;
END IF;
END
(I have not tested this.)
I am also assuming that your test for '' (empty string) instead of NULL is deliberate, and that you know that NULL is not the same as ''.
The key point in the UPDATE statement is that you should update first the column responseB, so that column responseA retains its original value which can be checked again when you try to update it:
UPDATE myTable
SET responseB = CASE WHEN TRIM(responseA) = '' THEN responseB ELSE 'foo' END,
responseA = CASE WHEN TRIM(responseA) = '' THEN 'foo' ELSE responseA END
WHERE Id = 1;

How can I check BIT datatype in MySQL?

I have a column in users table named permissions. Like this:
// users
+---------+-----------------+-------------+
| id | permissions | name |
+---------+-----------------+-------------+
| int(11) | bit(15) | varchar(20) |
+---------+-----------------+-------------+
| 1 | 001100001111101 | Jack |
| 2 | 111111111111111 | Peter |
| 3 | 110000000111011 | Martin |
+---------+-----------------+-------------+
As you can see, permissions column has bit(15) datatype. Each bit of that value determines one user ability. For example, the first bit refers to voting-ability, the second one refers to commenting-ability ant etc ...
Also I have a trigger BEFORE UPDATE which investigates that permission like this:
SELECT permissions INTO #deleting_permission FROM users WHERE id = new.deleter_id;
IF old.deleted <> new.deleted THEN
IF (IFNULL((#deleting_permission & b'10000000000' > 0), 0) < 1) THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "You cannot delete post";
END IF;
END IF;
And always it throws:
You cannot delete post
Even for #2 user which has 111111111111111 permission value. So what's wrong?
Noted that I update permissions column like this:
-- To give some specific accesses (permissions) to the user
UPDATE users SET permissions = b'111111000101011' WHERE id = ?
-- To give full access (permissions) to the user
UPDATE users SET permissions = -1 WHERE id = ?
You select the field active in the trigger to check permission, but according to your question, the rights are held in the permissions field.
Change your query to use the latter field:
SELECT `permissions` INTO #active_deleter FROM users WHERE id = new.deleter_id;
Also, your permissions field has 15 bits. The mask you use has only 10. You should check if you are testing the right bit in the first place.
If you want to work with the BIT type, then you'll need to use bitwise operators, not regular operators. For example, if you wanted to check if the user had the left most bit set as a permission, you could do an & operation:
SELECT permissions INTO #p FROM users WHERE id = new.deleter_id;
IF old.deleted <> new.deleted THEN
IF (b'100000000000000' & #p = 0)
THEN
SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "You cannot delete post";
END IF;
END IF;
The basic idea above is that the literal b'100000000000000' serves as a mask which can be used to detect if the left most bit (and only that bit) is set to 1. If it were set to 1, then the & operation would not return 0.
The reason why a user with 111111111111111 permissions has all rights is that any mask checking a bit would return true.

How to check record isn't containing specific string?

I have a table like this:
// mytable
+----+-------------------------------------------+
| id | col |
+----+-------------------------------------------+
| 1 | Peter|423421 , Alex Jon|61333 |
| 2 | Barmar|624321 |
| 3 | Jack|624321 , Ali|312331 , Leonard|624321 |
+----+-------------------------------------------+
I need to check second row if isn't containing 824326, then add this value , Sara|824326 in the end of that. Something like this:
| 2 | Barmar|624321 , Sara|824326 |
Now I want to know, how can I check that field isn't containing thins number 824326 ?
Here is my try, I just need to the condition (to check existing)
UPDATE mytable
SET col = CASE WHEN col = {:val doesn't exist} // <= How to write this condition?
THEN CONCAT(col,':newval')
ELSE SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "It is duplicate";
END,
WHERE id = 2;
Note1: :newval is this , Sara|824326 in the above query.
Note2: :val is this 824326 in the above query.
You have to use INSTR. Try this:
UPDATE mytable
SET col = CASE WHEN col INSTR(col, ':val') = 0
THEN CONCAT(col,':newval')
ELSE SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = "It is duplicate";
END,
WHERE id = 2;

KEA dhcp Mysql Backend

I have an issue with the new ISC DHCP, KEA, on the MySQL backend.
I want to store leases in my DB, it works but some info are not stored.
I obtain this kind of entry in my DB :
+------------+--------+-----------+----------------+---------------------+-----------+----------+----------+----------+
| address | hwaddr | client_id | valid_lifetime | expire | subnet_id | fqdn_fwd | fqdn_rev | hostname |
+------------+--------+-----------+----------------+---------------------+-----------+----------+----------+----------+
| 3232236052 | '° | NULL | 4000 | 2015-07-22 08:54:32 | 1 | 0 | 0 | │
+------------+--------+-----------+----------------+---------------------+-----------+----------+----------+----------+
The address field is the IP adress in decimal, I checked and it's the good one.
I didn't find how change the IP adress to IPv4 format and how store mac address in the hwaddr field in the KEA documentation.
If someone know how to do this I will be really grateful !
Thank you !
As per KEA documentation hwaddr field is VARBINARY. You should be able to see the value IP address and hwaddr using:
SELECT INET_NTOA(address), HEX(hwaddr), lease4.* FROM lease4;
I had a similar need to create KEA host reservations and populate the MAC and IP addresses as regular strings while still having the fields KEA uses updated automatically on an INSERT or UPDATE.
What I ended up doing is creating two new fields that would hold those string values ('hosts.dhcp_identifier_str' and 'hosts.ipv4_address_str'):
ALTER TABLE `hosts` ADD `dhcp_identifier_str` VARCHAR(12) NOT NULL AFTER `dhcp_identifier`;
ALTER TABLE `hosts` ADD `ipv4_address_str` VARCHAR(15) NULL DEFAULT NULL AFTER `ipv4_address`;
Then, I keep the corresponding fields that KEA uses ('hosts.dhcp_identifier' and 'hosts.ipv4_address') up to date by using BEFORE INSERT/UPDATE MySQL triggers:
DELIMITER //
DROP TRIGGER IF EXISTS `host_BINS`//
CREATE TRIGGER `host_BINS` BEFORE INSERT ON `hosts`
FOR EACH ROW BEGIN
IF (NEW.dhcp_identifier = '' AND NEW.dhcp_identifier_str != '') THEN
SET NEW.dhcp_identifier = UNHEX(UPPER(NEW.dhcp_identifier_str));
ELSEIF (NEW.dhcp_identifier_str = '' AND NEW.dhcp_identifier != '') THEN
SET NEW.dhcp_identifier_str = LOWER(HEX(NEW.dhcp_identifier));
END IF;
IF (NEW.ipv4_address IS NULL AND NEW.ipv4_address_str IS NOT NULL) THEN
SET NEW.ipv4_address = INET_ATON(NEW.ipv4_address_str);
ELSEIF (NEW.ipv4_address_str IS NULL AND NEW.ipv4_address IS NOT NULL) THEN
SET NEW.ipv4_address_str = CAST(INET_NTOA(NEW.ipv4_address) AS CHAR);
END IF;
END
//
DROP TRIGGER IF EXISTS `host_BUPD`//
CREATE TRIGGER `host_BUPD` BEFORE UPDATE ON `hosts`
FOR EACH ROW BEGIN
IF (NEW.dhcp_identifier_str != '' AND OLD.dhcp_identifier != UNHEX(UPPER(NEW.dhcp_identifier_str))) THEN
SET NEW.dhcp_identifier = UNHEX(UPPER(NEW.dhcp_identifier_str));
ELSEIF (NEW.dhcp_identifier != '' AND OLD.dhcp_identifier_str != LOWER(HEX(NEW.dhcp_identifier))) THEN
SET NEW.dhcp_identifier_str = LOWER(HEX(NEW.dhcp_identifier));
END IF;
IF (NEW.ipv4_address_str IS NOT NULL AND OLD.ipv4_address != INET_ATON(NEW.ipv4_address_str)) THEN
SET NEW.ipv4_address = INET_ATON(NEW.ipv4_address_str);
ELSEIF (NEW.ipv4_address IS NOT NULL AND OLD.ipv4_address_str != CAST(INET_NTOA(NEW.ipv4_address) AS CHAR)) THEN
SET NEW.ipv4_address_str = CAST(INET_NTOA(NEW.ipv4_address) AS CHAR);
END IF;
END
//
This works whether you INSERT/UPDATE an entry using the dhcp_identifier/ipv4_address or dhcp_identifier_str/ipv4_address_str pairs.
I'm sure you can use the same triggers for the 'lease4' table.
Hope that helps.

SQL query to remove certain text from each field in a specific column?

I recently recoded one of my sites, and the database structure is a little bit different.
I'm trying to convert the following:
*----*----------------------------*
| id | file_name |
*----*----------------------------*
| 1 | 1288044935741310953434.jpg |
*----*----------------------------*
| 2 | 1288044935741310352357.rar |
*----*----------------------------*
Into the following:
*----*----------------------------*
| id | file_name |
*----*----------------------------*
| 1 | 1288044935741310953434 |
*----*----------------------------*
| 2 | 1288044935741310352357 |
*----*----------------------------*
I know that I could do a foreach loop with PHP, and explode the file extension off the end, and update each row that way, but that seems like way too many queries for the task.
Is there any SQL query that I could run that would allow me to remove the file exentision from each field in the file_name column?
You can use the REPLACE() function in native MySQL to do a simple string replacement.
UPDATE tbl SET file_name = REPLACE(file_name, '.jpg', '');
UPDATE tbl SET file_name = REPLACE(file_name, '.rar', '');
This should work:
UPDATE MyTable
SET file_name = SUBSTRING(file_name,1, CHAR_LENGTH(file_name)-4)
This will strip off the final extension, if any, from file_name each time it is run. It is agnostic with respect to extension (so you can have ".foo" some day) and won't harm extensionless records.
UPDATE tbl
SET file_name = TRIM(TRAILING CONCAT('.', SUBSTRING_INDEX(file_name, '.', -1) FROM file_name);
You can use SUBSTRING_INDEX function
SUBSTRING_INDEX(str,delim,count)
Where str is the string, delim is the delimiter (from which you want a substring to the left or right of), and count specifies which delimiter (in the event there are multiple occurrences of the delimiter in the string)
Example:
UPDATE table SET file_name = SUBSTRING_INDEX(file_name , '.' , 1);