MYSQL Ordering an array - mysql

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.

Related

MySQL-query to retrieve MAX-value doesnt work for decimal numbers

I have a MySQL table that looks like this:
id layer l_to blank
1 1 10 xyz
0 0 5.5 xyz
I want to get the highest number of column-variable "l_to" that shares column-variable "blank".
I have tried the following SQL-query:
SELECT MAX(l_to), COUNT(layer),l_from FROM layers WHERE blank='xyz'
This works fine, if "l_to" of layer 1 is below 10. If it is ten, the query returns "l_to" from layer 0 (5.5).
Any Idea for why this is, and how can I retrieve the MAX?
#EDIT: Changing Datatype of "l_to" from VARCHAR to DECIMAL (5,1) got me the desired result. Thanks for the answers!
The datatype is not a number for field l_to so 5 is greater than 1 for a string. Probably a varchar. Change field l_to to a Decimal [1].
Only consider casting if you do not have control over the table structure as best practice is the data type reflects the data use in the world. This protects the data integrity of the database, provides helpful functions related to the datatype and ensures intuitive outcomes, like Max function. Casting as a work around for this query will only lead to downstream issues; refactor the structure now if you can.
Reference
Decimal data type suggested in comments by #Akina. Originally suggested float, but Decimal appears to reflect the Use Case better than float, given the limited examples shown.
Cast l_to from string to decimal
SELECT MAX(cast(l_to as DECIMAL(10,2)), COUNT(layer) from FROM layers WHERE blank='xyz'
Use a LIMIT query, and also cast the l_to column to decimal:
SELECT *
FROM layers
WHERE blank = 'xyz'
ORDER BY CAST(l_to AS DECIMAL(12.4)) DESC
LIMIT 1;

Fix JSON keys in PostgreSQL

I want to fix or validate keys for JSON object in PostgreSQL(v10.7).
For instance, I have a JSON object called service_config which looks like;
{"con_type": "Foo", "capacity": 2, "capacity_unit": "gbps"}
And I have table:
id(serial) service_name(char) service_type(char) service_config(JSON)
-----------+---------------------+---------------------+---------------------
1 | com | ethernet | {"con_type": "ddc", "capacity": 2, "capacity_unit": "gbps"}
2 | res | gpon | {"con_type": "ftth", "capacity": 1, "capacity_unit": "gbps"}
Now, whenever I insert row into the table, I want to make sure or validate that the service_config column contains all the keys that are mentioned above, no more, no less. However, there could be null value for the keys.
Is this possible in Postgres and/or is there any better way to do this?
Possible solutions:
1- Validate service_config at the backend API and make sure all the keys are there. (currently in place and working)
2- Write a function in Postgres to validate service_config on insert and update. (doable but tedious)
Limitation: I cannot add any extension in Postgres.
I want to make sure or validate that the service_config column contains all the keys that are mentioned above, no more, no less. However, there could be null value for the keys.
Turn them into columns.
JSON is nice when you need to just dump some data into a row and you're not sure what it's going to be. Now that you are sure what it's going to be, and you want more constraints, that's what columns do best.
alter table whatever add column con_type text;
alter table whatever add column capacity integer;
alter table whatever add column capacity_unit text;
update whatever set
con_type = data->'con_type',
capacity = data->'capacity',
capacity_unit = data->'capacity_unit';
alter table whatever drop column data
The columns will always be there. Their values may be null. You can add per-column check constraints and indexes. No additional validations are necessary.
If you still need json, use jsonb_build_object.
select
jsonb_build_object(
'con_type', con_type,
'capacity', capacity,
'capacity_unit', capacity_unit
)
from whatever;
And, if you need it for compatibility purposes, you can make this a view.
create view whatever_as_json
select
*,
jsonb_build_object(
'con_type', con_type,
'capacity', capacity,
'capacity_unit', capacity_unit
) as data
from whatever;
Note that I use text, not char, because there is no advantage to char in Postgres. See the tip in 8.3. Character Types
There is no performance difference among these three types, apart from increased storage space when using the blank-padded type, and a few extra CPU cycles to check the length when storing into a length-constrained column. While character(n) has performance advantages in some other database systems, there is no such advantage in PostgreSQL; in fact character(n) is usually the slowest of the three because of its additional storage costs. In most situations text or character varying should be used instead.

MySQL Query conditional find nth element in column string

I have a MySQL table setup where one column's values are a string of comma-separated True/False values (1s or 0s). For example, in the column, one field's value may be "0,1,0,0,0,0,1,1,0" and another may be "1,0,0,1,1,1,0,0,0" (note: these are NOT 9 separate columns, but a string in one column). I need to QUERY the MySQL table for elements that are "true"(1) for the "nth element" of that column's value/string.
So, if I was looking for rows, with a specific column, where the 3rd element of the column's value was 1, it would produce a list of results. So, in this case, I would only be searching for "1" in the fth place (12345 = X,X,X...) of the string (X,X,1,X,X,X,X,X,X,X). How can I query this?
This is a crude example of what I am trying to do ...
"SELECT tfcolumn FROM mytable WHERE substr({tfcolumn}, 0, 5)=1"
{tfcolumn} represents the column value
5 represents the 5th position of the string
=1 represents what I need that position to equal to.
Please help. Thanks
You can't. Once you put a serialized data type into a column in SQL (like comma separated lists, or JSON objects) you are preventing yourself from performing any query on the data in those columns. You have to pull the data in a different way and then use a program like python, VB, etc to get the comma separated values you are looking for.
Unless you want to deal with trying to make this mess of a query work...
I would recommend changing your table structure before it's too late. Although it is possible, it is not optimized in a format that a DBMS recognizes. Because of that the DBMS will spend a significant amount of time going through every record to parse the csv values which is something that it was not meant to be doing. Doing the query in SQL will take as much time (if not more time) than just pulling all the records and searching with a tool that can do it properly.
If the column contains values exactly like the ones you posted, then the Nth element is at the 2 * N - 1 position in the comma separated list.
So do this:
SELECT tfcolumn
FROM tablename
WHERE substr(tfcolumn, 2 * 5 - 1, 1) = '1'
Replace 5 with the index that you search for.
See the demo.
Or remove all commas and get the Nth char:
SELECT tfcolumn
FROM tablename
WHERE substr(replace(tfcolumn, ',', ''), 5, 1) = '1'
See the demo.
Try this
if substring_index(substring_index('0,1,0,0,0,0,1,1,0',',',3),',',-1)='1'
The first argument can be your column name. The second argument (',') tells the function that the string is comma-separated. The third argument takes the first 3 elements of the string. So, the output of inner substring_index is '0,1,0'.
The outer substring_index has -1 as the last argment. So, it starts counting in reverse direction & takes only 1 element starting from right.
For example, if the value in a particular row is '2,682,7003,14,185', then the value of substring_index(substring_index('2,682,7003,14,185',',',3),',',-1) is '7003'.

Select a part of a string in MySQL

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.

Alphabetically ordering records with "The", "A", "An" etc at the beginning of varchar field

I'm looking for both MySQL and PostgreSQL solutions for this kind of problem.
Say I have a number of records with a title field. The titles are book or movie titles, like "The Cat in the Hat" and "Robin Hood". But while the titles must be displayed in their original form, they ought to be sorted in the way that libraries sort them, which is by moving any article, like "The" or "An" to the end of the title.
So "The Cat in the Hat" is sorted as if it were "Cat in the Hat, The".
What's the best way either to design the schema or write the query so that these records are sorted by title in the same way that libraries sort the title? (I also wish I knew the technical term for this type of ordering by title.) Also, what performance considerations should I be aware of and what indexes should I create?
Why don't you just add a "title_prefix" field to the table and move all these "the" and "a" strings there? When you're ordering you would use the "title" field, and when you are presenting the title you could do the concatenation in any way you wish.
Create a custom function that (sortableTitle, perhaps?) that will modify strings starting with your unwanted words. Finish your query statement with order by sortableTitle(title). This will incur an extra CPU cost, though you'll have to benchmark to know how much so.
You could create an extra column (sortTitle) that is populated by a trigger. This will take up some space, but then your server will be able to sort rows by an index.
Excepting the above, you cannot (without modifying the database server code) directly create an index that is in the order you want. As far as I can tell, that applies to both MySQL and PostgreSQL.
iTunes achieves this by having a second field in which the title is stored in the desired sorting format and sorting on this instead of title. It does sound like the cheap way out, but when you consider the performance implications of doing string manipulations on every title every time you do a select statement that orders by title, against doing string manipulations each time you insert or update the title, it does make sense.
Select * from TitleTable
Order by
Case when substring(title,0,4) = 'The ' then substring(title, 4, len(title)-4)
when substring(title,0,3) = 'An ' then substring(title, 3, len(title)-3)
when substring(title,0,2) = 'A ' then substring(title, 2, len(title)-2)
else title
end
I would suggest you split the title field in two fields: mainTitle and pre.
When a title is added, check if it starts with "A", "The" or other prefixes and split it (perhaps with a trigger) into the two fields. Your table would look like this:
| pre | mainTitle |
|-----|----------------|
| The | Cat in the Hat |
| A | Space Odyssey |
| | Eyes Wide Shut |
So, you can have an index on the mainTitle field and use it for sorting.
When you want to show the full title, concat the two fields, in either of the two forms.
If you choose this way, you'll have to modify accordingly the code for when a user gives a title to search in your table. The given title will have to be split the same way before searching the mainTitle field.
You'll have to be very, very careful with the code (trigger or other) that does the spliting so some special cases are caught correctly. You wouldn't want to have the A = B or the A B C: learn the alphabet books shown and sorted as = B, A and B C: learn the alphabet, A