MySQL to update an XML node value - mysql

This is somewhat related to this MySQL to update an XML attribute but this time I want to update the node value. I have the following XMLfragment which is in marcxml column:
<leader>00227nz a2200109n 4500</leader>
<controlfield tag="001">1</controlfield>
...
<controlfield tag="005">20091210091717.0</controlfield>
...
I want to update the controlfield value tag 001 such that it becomes a number based on a query. So like this:
<leader>00227nz a2200109n 4500</leader>
<controlfield tag="001">10</controlfield>
...
<controlfield tag="005">20091210091717.0</controlfield>
...
I have initially the following mysql query:
UPDATE auth_header SET marcxml = UpdateXML(marcxml, '//controlfield[#tag="001"]', CONCAT('<controlfield tag="001">', '10', '</controlfield>')) WHERE Extractvalue(marcxml, '//controlfield[#tag="001"]') ='169625';
The table is auth_header and it has authid as primary key (but I guess this does no matter) and it has marcxml column where the xml is stored. The query gives me '0 rows affected.' so it seems it does not work.
Thanks in advance and cheers!

Looking at the discussions here MySQL to update an XML attribute and mysql site https://dev.mysql.com/doc/refman/5.1/en/xml-functions.html#function_updatexml, the query:
UpdateXML(xml_target, xpath_expr, new_xml)
should do the trick.
The xml_target is marcxml in the question's case. The xpath_expr is '//controlfield[#tag="001"]' which is the node that needs editing. The new_xml is to concat , the digit desired, and the closing statement . And lastly, the where expression is also the same with xpath expression above.
Hence:
UPDATE auth_header SET marcxml = UpdateXML(marcxml, '//controlfield[#tag="001"]', CONCAT('<controlfield tag="001">', '10', '</controlfield>' )) WHERE Extractvalue(marcxml, '//controlfield[#tag="001"]') ='169625';

Related

Update all with a join of child attributes

How I can perform a mass update for cache_attribute :cached_comment_infos stored on my Posts
It would be something like this:
Post.joins(:comments).where(id: post_ids).update_all(
cached_comment_infos: self.comments.pluck(:author_name, :reference).map { |v| v.join("/") }.join(", ")
)
The output expected for each Post should be something like this : "John Doe/3242, Tom Jed/6264"
I don't think you want update_all. Update_all will update all records to one value.
https://apidock.com/rails/v4.0.2/ActiveRecord/Relation/update_all
Example given in the docs is setting all Customer's wants_email to true
Customer.update_all wants_email: true
You can loop over the posts and update each post in a unique sql statement
Post.joins(:comments).where(id: post_ids).each do |post|
post.cached_comment_infos = post.comments.pluck(:author_name, :reference).map { |v| v.join("/") }.join(", ")
post.save
end
If you really want it to happen in one SQL statement you could look into the gem activerecord-import. You would need to use their 'duplicate key update' feature: https://github.com/zdennis/activerecord-import#duplicate-key-update

How does one update an attribute and add a new attribute in a JSONB column in YugaByte DB?

I am trying YugaByte's Cassandra API (YCQL) and interested in using the JSONB data type extensions.
But I am having trouble both updating an attribute in an existing JSONB column as well as adding a new attribute to an existing JSONB column.
Is this supported in YugaByte? Here is what I tried:
Consider the following example whichhas have one row with a simple key and JSONB column.
cqlsh:k> CREATE TABLE T (key int PRIMARY KEY, value jsonb);
cqlsh:k> INSERT INTO T(key, value) VALUES(1, '{"author": "Charles", "title": "Hello World"}');
cqlsh:k> SELECT * FROM T;
key | value
-----+--------------------------------------------
1 | {"author":"Charles","title":"Hello World"}
(1 rows)
So far so good.
If I try to update an existing attribute inside the doc, I see the following error:
cqlsh:k> UPDATE T SET value->'author' = 'Bruce' WHERE key=1;
InvalidRequest: Error from server: code=2200 [Invalid query] message="SQL error: \
Invalid Arguments. Corruption: JSON text is corrupt: Invalid value.
If I try to add a new attribute into an existing JSONB attribute, I get the following error;
cqlsh:k> UPDATE T SET value->'price' = '10' WHERE key=1;
InvalidRequest: Error from server: code=2200 [Invalid query] message="SQL error: \
Execution Error. Could not find member:
Is this supported, and if so what is the correct syntax?
When updating a string value you must enclose the new value in double quotes inside the single quotes. For example:
cqlsh:k> UPDATE T SET value->'author' = '"Bruce"' WHERE key=1;
cqlsh:k> SELECT * FROM T;
key | value
-----+------------------------------------------
1 | {"author":"Bruce","title":"Hello World"}
(1 rows)
Regarding the second question on ability to add new attributes:
For UPDATE, currently (as of 1.1) YugaByte DB allows updating specific attributes if that attribute/field already exists, but does not allow addition of new attributes into an existing JSONB column. If you need to the latter, you need to read the old value into the app, and write the new json in its entirety.

Update Statement with filter, sortBy, map and take

I am writing the following update statement against a MySql database:
Orders.filter(_.cookie === cookie).sortBy(_.id.desc).map(_.payTypeId).take(1)
sortBy and take(1) are being used to ensure I'm only updating the most recent order for a particular cookie. This query works fine as a regular SELECT, this issue here is using it as an update statement.
The error I'm getting:
[SlickException: A query for an UPDATE statement must resolve to a comprehension with a single table -- Unsupported shape: Comprehension s2, Some(Apply Function =), None, ConstArray((Path s2.id,Ordering(Desc,NullsDefault))), None, None, Some(LiteralNode 1 (volatileHint=false)), None, false]
If I remove the take(1) clause, everything works fine. Why I am I getting this error?
Github seems to have a discussion about this, with no solution.
Workaround using plain SQL
(This query returns a Vector[Int])
val updateQuery =
sql"""
UPDATE
orders
SET
orders.pay_type_id = $id
WHERE
orders.cart_cookie = $cookie
ORDER BY id desc
LIMIT 1
""".as[Int]

MySQL query to append key:value to JSON string

My table has a column with a JSON string that has nested objects (so a simple REPLACE function cannot solve this problem) . For example like this: {'name':'bob', 'blob': {'foo':'bar'}, 'age': 12}. What is the easiest query to append a value to the end of the JSON string? So for the example, I want the end result to look like this: {'name':'bob', 'blob': {'foo':'bar'}, 'age': 12, 'gender': 'male'} The solution should be generic enough to work for any JSON values.
What about this
UPDATE table SET table_field1 = CONCAT(table_field1,' This will be added.');
EDIT:
I personally would have done the manipulation with a language like PHP before inserting it. Much easier. Anyway, Ok is this what you want? This should work providing your json format that is being added is in the format {'key':'value'}
UPDATE table
SET col = CONCAT_WS(",", SUBSTRING(col, 1, CHAR_LENGTH(col) - 1),SUBSTRING('newjson', 2));
I think you can use REPLACE function to achieve this
UPDATE table
SET column = REPLACE(column, '{\'name\':\'bob\', \'blob\': {\'foo\':\'bar\'}, \'age\': 12}', '{\'name\':\'bob\', \'blob\': {\'foo\':\'bar\'}, \'age\': 12, \'gender\': \'male\'}')
Take care to properly escape all quotes inside json
Upon you request of nested json, i think you can just remove last character of the string with SUBSTRING function and then append whatever you need with CONCAT
UPDATE table
SET column = CONCAT(SUBSTRING(column, 0, -1), 'newjsontoappend')
modify Jack's answer. Works perfectly even column value is empty on first update.
update table
set column_name = case when column_name is null or column_name =''
then "{'foo':'bar'}"
else CONCAT_WS(",", SUBSTRING(column_name, 1, CHAR_LENGTH(column_name) - 1),SUBSTRING("{'foo':'bar'}", 2))
end

mysql update with regexp

I want to remove something from my table 1) 32) 121) 1000)... the format is number + )
I tried this code.
UPDATE articles SET
title= REPLACE(title,'\d)', '' )
WHERE title regexp "\d)*"
Nothing happened in phpmyadmin, how to write correct? Thanks.
You can't: Mysql doesn't support regex-based replace.
See this SO question for a work-around.
Finally, I use some php to solve this problem with a quickly method.
for ($i=1; $i<=9999; $i++){
$my_regex = $i.')';
mysql_query("UPDATE articles SET title = REPLACE(title,'".$i."', '' ) where title like '%".$i."%'");
}
I have unique requirement where I need to replace inactive owner username. Where username contians INACITVE followed by village id. So I have used concat() funacion inside replace() function to replace dynamically.
update Owner set username = replace(username, concat('_INACTIVE_',village_id) ,'')
where village_id = 3363010;
As an alternative, depending on the size of the table, you could do a workaround with substring function.