I have a MySQL table with a string column as,
ID String
----- -----------------------------------------
1 {"Type":"new", "Node":"{Status=New, Properties=[{PropertyValue=Samp, PropertyRefernceTypeID=1, PropertyTypeID=26}, {PropertyValue=25, PropertyRefernceTypeID=1, PropertyTypeID=33}]}"}
2 {Type":"new", "Node":"{Status=New, Properties=[{PropertyValue=25, PropertyRefernceTypeID=1, PropertyTypeID=33}, {PropertyValue=168-3, PropertyRefernceTypeID=1, PropertyTypeID=103}]}"}
Now how can I select only the PropertyValue of PropertyTypeID=33 in MySQL select Query? It is possible by using substring() method only if the length of all the strings are equal but in my case the length may vary for different ids.
This is why it is considered bad practice to store JSON, serialized arrays/objects, or just multiple values in a database field, unless for some reason you would never need anything inside it.
The field should always be atomic. You should follow database normalization guidelines.
Therefore your database should be something like:
ID | PropertyValue | PropertyReferenceTypeID | ProductTypeID
Then you would never have this problem, you would just do SELECT PropertyValue FROM Properties WHERE ID=1
But for now, to avoid this trouble, if you are using an app language, just get the string and decode it. For example, in PHP:
$array = json_decode($str);
$PropertyValue = $arr['Node']['Properties']['PropertyValue'];
P.S. Your json still isn't completely valid with it's nesting.
Related
Im little bit stucked with my SQL query.
I've got a table with rows that can be identified by id or hash string...
id
short
title
1
asdadasdsd
foo
2
1qweqweqwe
bar
3
yxcyxcyxcy
baz
So SQL is quite easy...
SELECT * FROM table WHERE id=<identifier> OR hash=<identifier>
What I found out is that when my identifier is hash and begins with number which could be found in the id column, MYSQL returning me "wrong" row.
For example when my identifier is "1qweqweqwe" result is row 1.
I think the reason for that is it converts my hash string into integer maybe? Is there a way how to disable this behaviour?
Or the only way is to regenerate all hashes into new formats without numbers in it?
Thank you for any clarification :)
Petr
No, you do not have to regenerate the hashes. If both the id and hash match and you prefer
to pull row based on hash, then you could have the hash as the first match column condition. Basically it goes with the first match condition that is found to be true.
Also, I suppose you are already adding quotes to the hash string in the query. If not please do, as it will validate as a string then.
SELECT * FROM table WHERE hash='<identifier>' OR id=<identifier>
You seem to be passing the identifier in as a string -- because it is. But then you are comparing to a number (the id) and the string parameter is converted to a number. MySQL does so by converting the leading digits, if any.
I don't like the logic of passing in a string for an identifier, so I would really suggest that you fix the calling logic and call either:
WHERE id = <int identifier>
or:
WHERE hash = <string identifier>
But if you want to keep your current version, you can convert to a string:
WHERE CAST(id AS CHAR) = <identifier> OR hash = <identifier>
So, I have a table that I wish to look like this
id | path
----------- -------------------------
000000 | images/0/0/0/0/00.jpg
000001 | images/0/0/0/0/01.jpg
000002 | images/0/0/0/0/02.jpg
where id is an auto_increment number and path is a string. I wish to make the column path be auto_generated but I couldn't find a SQL function to do that. If I were to use Python, I can easily calculate path with this expression
ID = 1
path='images/'+'/'.join(str(ID).zfill(6))[::-1].replace('/','',1)[::-1]+'.jpg'
However, I have no idea how to do that with SQL. I understand that I can retrieve the ID from database and then use a script to calculate the path, but I think it may be better just to cache all the results in the database so that my script can work faster.
Update:
I can, of course, write a script to handle this, be it a js, Python or PHP. But since I don't know what the id would be before I insert a row, I need to do an INSERT, then SELECT, to find out the id; then I have to UPDATE the calculated path. In total this will be three queries. What I wanted to do was to simplify the whole thing into one query, for I'm worrying about performance issue.
I don't know if I understand correctly but it looks like you want that the auto_generated path looks like a id but separated by "/". If it's this why don't you use php str_split to convert id string in other strings, and this way you can generate the path automatically.
For example:
<?php
//first you must do a mysql select to display all fields
//this define your string
$str = $id;
//this cut string with a limit of 1 character
$arr = str_split($str, 1);
//define a string with the path format you want
$path = "images/$arr[0]/$arr[1]/$arr[2]/$arr[3]/$arr[4]/$arr[5]$arr[6].jpg";
//now do a mysql update
UPDATE db_name SET path=$path WHERE id=$id;
?>
And the result will be a path updated to, where id = 000001, images/0/0/0/0/01.jpg
This is my result
+----------------------------------------------------------------------------------------------------------------------------------------------+
| SUBSTRING(COLUMN_TYPE,5) |
+----------------------------------------------------------------------------------------------------------------------------------------------+
| ('Sedan','Hatch','Convertable','Van','Coupe','Light Truck','People Mover','SUV','Ute','Wagon','Cab Chassis','Sample Body','Body 4','BOdy 5') |
+----------------------------------------------------------------------------------------------------------------------------------------------+
This is my query
SELECT SUBSTRING(COLUMN_TYPE,5) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='Ad_tbl' AND COLUMN_NAME='body_type'
I want the array to be ordered the other way.. So the Sedan would be in the last of the array instead of first.. Already Tried ORDER BY either ASC or DESC but no luck
You're querying the list of items in an ENUM definition, and you want to change the order? You can't do that without using ALTER TABLE to change your ENUM.
The order of items in an ENUM is related to the physical storage of the values. 'Sedan' is 1, 'Hatch' is 2, 'Convertable' (sic) is 3, etc. Changing the order of these strings requires changing the enumeration values.
Of course, you could change the order of displaying the strings in your application code. But this means parsing out the items from that list, splitting on comma, removing quotes and parens, etc.
But doing similar text-parsing in pure SQL will be an exercise in frustration, or at least, it'll be a huge waste of time.
This awkwardness of fetching the items in an ENUM definition is one of the reasons MySQL's ENUM data type is evil.
If you want to control the sort order without redefining the table,
you'll be better off using a lookup table instead of an ENUM.
I have a table :
table
---------------------
id | value
---------------------
1 | invalid_json
---------------------
2 | valid_json
---------------------
3 | invalid_json
---------------------
First of all, value is in varchar type not really declared as json, and it has some reasons why it is set up like that. Anyway, my question is about the possibility, and if possible how. Is it possible to create an sql to find only the rows that contains a VALID json formatted data even though the column data type is var char?
A sort of :
"select * from table where (condition that data is a valid json)";
As a_horse_with_no_name stated, you can write a function trying to cast to json and return a result based on the success of that operation.
CREATE FUNCTION is_json(varchar) RETURNS boolean AS $$
DECLARE
x json;
BEGIN
BEGIN
x := $1;
EXCEPTION WHEN others THEN
RETURN FALSE;
END;
RETURN TRUE;
END;
$$ LANGUAGE plpgsql IMMUTABLE;
Making it IMMUTABLE will make it operate quickly for repeated strings (such as an empty string for example), but it highly depends on the size of your table.
Then you can query your data.
SELECT * FROM table WHERE is_json(value);
If the table is big and you are about to repeat that query a lot of times, I would add an additional is_json boolean field to the table. Then create a trigger/rule to check the validity upon INSERT/UPDATE on that table.
However, having mixed data types in the same column is not a good idea, mind changing your data structure in case you are considering such a scenario.
I recently solved a similar problem by doing a simple check on the string for curly braces:
WHERE value LIKE '{%}'
This of course depends on the data you expect, and will not match all valid JSON nor exclude all non-JSON. In my case I had a field that used to take a simple character string (still present in old records) but now takes a JSON object wrapped in curly braces. If your case is like mine--you know some specifics about what the valid and invalid data look like--you might do it this way.
I'm transferring a MySQL table to MongoDB. There is a primary key id in my MySQL table and I want this id to be converted to the _id in MongoDB.
I use php's MongoCollection::insert( $mysql_array );
However it doesn't work because if I set $mysql_array['_id'] it is seen by mongo as a String instead of a MongoId. I tried $mysql_array['_id'] = new MongoId( $id ) but it doesn't allow me to override the default _id value. I saw that all my MySQL's integer columns are converted to string by MongoCollection::insert(). If I could set MongoCollection::insert() to correctly transfer an integer it would maybe work.
typecast the _id to a integer value like this...
(int) $mysql_array['_id']
You'll find yourself doing this a lot in mongoDB
The ObjectId is a special type in Mongo, but the _id property doesn't have to be of this type. You can't coerce a string or number into an ObjectId, and you shouldn't.
I assume the problem as you perceive it is that your insert worked, but when you looked at the data in the database the _id property didn't look like _id: ObjectId("1234") (if the original ID was 1234). This is as it should be, and it's perfectly fine.
The idea with ObjectId is that it has a predefined structure that makes it guaranteed (mostly) to be unique across a Mongo cluster, but this also means that it has to have this structure, otherwise it is not an ObjectId.
You also mention that all your integer columns are converted to strings. PHP and PHP libraries, are notoriously sloppy when it comes to types, so make sure that it's not the case that the values are already strings when they come from the MySQL result set. Worst case you have to explicitly cast the values before inserting them into Mongo.
You won't be able to convert an arbitrary String value into an Mongo ObjectId due to its specific characteristics (12 bytes -> 24 chars generated from 4 bytes timestamp, 3 bytes client hostname, 2 bytes PID, 3 bytes inc value).
Either you abandon using the MongoId type in your collection's _id-fields and use your MySQL-ID as a string instead (which is not a problem and makes the most sense) or you let Mongo to generate the documents' _id for you, which is also a suitable solution if you want to be able to use the MongoId functions (assuming you're working with PHP):
The MongoId class
Choosing the second solution you still are able to store your MySQL-IDs in another field of the doc, like id or *mysql_id* to reference them later.
Concerning your question about (int) and (string) values: Are you sure they come as a PHP integer from your MySQL DB? If so, they usually should be stored as integers in Mongo. Check it with a var_dump() and in case of incompatibility cast it with an (int). Maybe it would be helpful if you post your select/insert code...
Use MongoCollection::save() and your array should work.