Updating multiple columns depending on 1 CASE-condition - mysql

I have a program that needs to synchronize it's frequently changing values (in temporary memory) with a database. The critical key (not primary!) in that table is the column id. My program changes the id but keeps the old id in memory, too.
Now, I would like to update several specified columns for multiple records/rows in one single statement. Furthermore, it should be reasonably fast for 5 up to 10 of such statements in 1 second with 4 GB RAM and ~ 50 MBit/s connection that is not only used for these sql-calls.
My sql-specifications
Server: 127.0.0.1 via
TCP/IP
Software: MySQL
Software version: 5.5.27 - MySQL Community Server (GPL)
Protocol version: 10
Server charset: UTF-8 Unicode (utf8)
I tried to use brackets...
UPDATE someTable
SET (id, name) = CASE id
WHEN 1 THEN (111, "Dr. Frankenstein")
WHEN 2 THEN (222, "the Monster")
WHEN 3 THEN (333, "Mr. X")
ELSE (id, name) END
WHERE id IN (1, 2, 3)
...which simply results in the following error:
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(id, name) = CASE id WHEN 1 THEN (111, "Dr. Frankenstein") WHEN 2 THEN (222,' at line 2
Know I wonder: Is there a way to do it in just one statement with the current syntax? Would it be feasable that way or should I just split it into multiple statement which is ugly in terms of the program that makes the sql-calls.
Answers and suggestions are welcome!

A case statement only returns one value:
UPDATE someTable
SET id = (CASE id WHEN 1 THEN 111 WHEN 2 THEN 222 WHEN 3 THEN 333 ELSE id END),
name = (CASE id WHEN 1 THEN 'Dr. Frankenstein'
WHEN 2 THEN 'the Monster'
WHEN 3 THEN 'Mr. X'
ELSE name
END)
WHERE id IN (1, 2, 3);
For performance, be sure you an an index on id. This will help with finding the records to update. Do note that changing the id value requires updating the index, which can be a bit longer than a normal update. However, expecting 5-10 transactions a second is reasonable.

Hope this works:
UPDATE someTable
SET id = CASE
id
WHEN 1 THEN 111
WHEN 2 THEN 222
WHEN 3 THEN 333
ELSE id END
,
name = CASE
id
WHEN 1 THEN "Dr. Frankenstein"
WHEN 2 THEN "the Monster"
WHEN 3 THEN "Mr. X"
ELSE name END
WHERE id IN (1, 2, 3)

Related

MySQL Error: right syntax to use near ''

I have the this query:
SELECT owner,
CASE WHEN id IN (1,2,4) THEN 25
ELSE
CASE WHEN owner = 25 THEN NULL
END as owner
FROM board2
and get this error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'as owner
FROM board2' at line 9
What is my mistake?
Example:
id - owner - result owner
2 - 4 - 7 (id is selected and owner has changed
3 - 7 - 7 (id is selected and owner has changed
4 - 7 - NULL (id isnt selected, owner is the same like the new one, so owner is set to NULL
5 - 4 - 4 (id isnt selected, owner isnt the new owner, no changes
x = (2, 3) (The ids which have to changed)
y = 7 (The new owner)
Not sure this is the logic you want (you have 2 CASEs, not 1 with different alternatives):
SELECT owner,
CASE
WHEN id IN (1,2,4) THEN 25
ELSE
END
CASE
WHEN owner=25 THEN NULL
ELSE
END
FROM board2
If not then you can't have the WHEN in the 2nd CASE and may want to go with IF, ELSEIF, ELSE, END IF.
You have syntax issue in the case-when , in addition you are selecting owner 2 times which is also again an error
It should be something as
SELECT owner,
CASE
WHEN id IN (1,2,4) THEN 25
WHEN owner = 25 THEN NULL
else 'ABC' ---- better to provide else other wise non-matching data will be selected as null, give something as you want
END as `some_other_col_name` --- a different col name since owner is already selected on the top
FROM board2

Rails join two tables or replace numbers by the words

I want to write logs to the database and I have often repeated words (syslog, sms, voice, email), I need to instead these words insert numbers (syslog - 1, sms - 2, voice - 3, email - 4), so as not to clog the base repeating words. And then select the words instead numbers
Excuse me, i asked the question wrong. I have data already saved, and i need select it to my index.html.erb. query must be like this: "SELECT triggerid, severity, status, types.name FROM logs, types WHERE logs.type_id = types.id;" logs.type_id = (1, 2, 3, 3, 2, 2, 1), types.name = (email, sms, syslog, voice), types.id = (1, 2, 3, 4,) after query myst be: types.name = (email, sms, syslog, syslog, sms, sms, email). How can i use activerecord fo this result?
I'm not sure what field name/table your wanting to store these numbers in so I'm going to just call it 'FIELD_NAME' and 'TABLE'. Keeping it in mysql you could do this:
Retrieve:
SELECT TABLE.*, case FIELD_NAME WHEN 'syslog' THEN 1 WHEN 'sms' THEN 2 WHEN 'voice' THEN 3 WHEN 'email' THEN 4 ELSE null END as FIELD_ALIAS FROM TABLE
the result will have an attribute of FIELD_NAME with the int value, and an attribute of FIELD_ALIAS with the text version.
Inserting (STR_VALUE is the string version of the field):
INSERT INTO TABLE_NAME (FIELD_NAME) VALUES (case STR_VALUE WHEN 'syslog' THEN 1 WHEN 'sms' THEN 2 WHEN 'voice' THEN 3 WHEN 'email' THEN 4 ELSE null END)
I believe a better alternative though would be to offload this to your model. for simplicity I'm going to refer to your integer version as 'int_value'.
class Address < ActiveRecord::Base
before_save :convert_int_value
def str_value
case self.int_value
WHEN 1 THEN 'syslog'
WHEN 2 THEN 'sms'
WHEN 3 THEN 'voice'
WHEN 4 THEN 'email'
end
protected
def convert_int_value
return nil unless int_value.is_a?(String)
self.int_value = case self.int_value
WHEN 'syslog' THEN 1
WHEN 'sms' THEN 2
WHEN 'voice' THEN 3
WHEN 'email' THEN 4
end
end
Anytime you want the int version, just call int_value on your model. If you want the string call str_value. Additionally you can store the string version, in your int_value and before the record is saved it will convert it for you. This way all you have to pass mysql is an integer.
Update per comment
If your associations are set up correctly (Log belongs_to :type)
Log.includes(:type).each do |l|
l.status
l.type.name #> email, sms, or syslog
end
I realized after the fact there is another solution as well, I think this is more what you were looking for
Log.joins(:type).select('triggerid, severity, status, types.name')
Alt1
Log.joins(:type).select('log.*, types.name')
Alt2
Log.joins('JOINS types ON types.id = logs.type_id').select('log.*, types.name')
are you after one address per client?:
Client.joins(:addresses)
or every address with client info?:
Adress.join(:client)

MySQL and Bit Bashing

I'm using Bit bashing on SQL for user rights, like UNIX rights :
1 - 001 - Execute
2 - 010 - Write
4 - 100 - Read
So if I have right 6 ( 2+4 or 110) I can write AND read but I can't execute. On my case i have many more rights, so my users can have the value "128" for rights.
How can I do a query and ask all used with a right (like 2 for UNIX example)
SELECT * from user WHERE user_right ?? '2'
The users rights must be 2 (010), 3 (011), 6 (110) or 7 (111) ?!
Thank you all and sorry for my English :/
128 would mean there were 7 total user rights:
1 1 1 1 1 1 1 = 0177 = 0x7F = 127
MySQL already has an abstraction for this, known as the SET type, which essentially maps your named/visible value for the right to a binary value. So if you had a set column user_right:
('Execute', 'Write', 'Read')
They would actually be stored on the server backend as:
(1, 2, 4)
This allows for normal queries like:
SELECT * FROM user WHERE user_right LIKE '%Write%';
or using FIND_IN_SET:
SELECT * FROM user WHERE FIND_IN_SET('Write',user_right) > 0;
or direct bitset operations:
SELECT * from user WHERE user_right & 2;
to name a few. If you add an ORDER BY user_right to your select queries, it returns them in their numerical (not alphabetical) order, but otherwise you can quickly determine the values of a user with:
SELECT user_name, user_right FROM user;
and seeing that the returned value is:
'some user' | Read,Write
while still having the benefits of bitwise operations for very fast filtering, such as:
SELECT user_name, user_right FROM user WHERE user_right & ~1;
to return all users with read and write permissions that do not have execute rights.

How to delete similar rows from my table in MySQL?

I'd like to perform a cleanup in one of my MySQL Drupal tables to remove duplicate values stored. Here is the structure:
NID VID DELTA FIELD_VALUE
100 100 0 foobar
100 100 1 foobar
101 101 0 barbar
101 101 1 barbar
102 102 0 foofoo
My goal is to remove rows with bigger DELTAs if a row with the same NID, VID, FIELD_VALUE exists with smaller DELTA.
My first attempt was the following query:
delete from mytable a where a.delta=1 and 1=(select count(nid) from mytable b where b.nid=a.nid and b.vid=a.vid and b.delta=0 and b.field_value=a.field_value)
Unfortunately the DB says: (MySQL 5.1.65-cll)
#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'a where a.delta=1 and 1 = (select count from `field_value` b where' at line 1
which is not very helpful to me.
UPDATE:
A deleted answer told me that MySQL does not support alias in delete statements, but removing aliases did not help. The subquery is ok, checked separately.
How about this one?
DELETE a
FROM mytable a
JOIN mytable b ON (a.nid = b.nid
AND a.vid = b.vid
AND a.field_value = b.field_value)
WHERE a.delta > b.delta
(don't forget to backup your data)
Join the table to itself on three columns (NID, VID, FIELD_VALUE) and SELECT the MAX value for DELTA.
This will work as long as you don't have any other columns involved.
Here is a good example for this

Using IN clause in sql server

My query is like below.I want to select values if Type = 1 and subtype = 1,3 or 2.
select sum(case when Type = 1 and SubType in (1, 3 or 2) then 1 else 0 end) as 'WorkStations'
Is this right way?
Since you're only trying to get a count of the workstations that meet the criteria as far as I can see:
SELECT COUNT(*) AS Workstations FROM MyWorkStationTable WHERE Type = 1 AND SubType IN (1, 2, 3)
Also, an IN clause is by nature already an OR. It is neither valid syntax nor necessary to state it.
If you're simply counting records, your best bet is to use the COUNT function provided by SQL Server. Consider using the following:
SELECT COUNT(*) FROM [Table] WHERE TYPE = 1
AND (SUBTYPE = 1
OR SUBTYPE = 2
OR SUBTYPE = 3)
It is best to avoid using 'IN' as it can lead to unnecessary calls to the SQL engine.
SELECT COUNT(*) [Workstations] FROM [YourTable] t WHERE t.Type = 1 AND t.SubType IN (1, 2, 3)
Try avoiding IN Predicates and instead use Joins because it Iterate unnecessarily despite of the fact that there is just one/two match. I will explain it with an example.
Suppose I have two list objects.
List 1 List 2
1 12
2 7
3 8
4 98
5 9
6 10
7 6
Using IN, it will search for each List-1 item in List-2 that means iteration will happen 49 times !!!