In MySQL/MariaDB the most efficient way to store uuid is in a BINARY(16) column. However, sometimes you want to obtain it as a formatted uuid string.
Given the following table structure, how would I obtain all uuids in a default formatted way?
CREATE TABLE foo (uuid BINARY(16));
The following would create the result I was after:
SELECT
LOWER(CONCAT(
SUBSTR(HEX(uuid), 1, 8), '-',
SUBSTR(HEX(uuid), 9, 4), '-',
SUBSTR(HEX(uuid), 13, 4), '-',
SUBSTR(HEX(uuid), 17, 4), '-',
SUBSTR(HEX(uuid), 21)
))
FROM foo;
MySQL 8 adds two new UUID functions:
UUID_TO_BIN
BIN_TO_UUID - this is the one you're looking for
So:
SELECT BIN_TO_UUID(uuid) FROM foo
In earlier (prior to 8) versions you can create a function in MySQL like the following:
CREATE
FUNCTION uuid_of(uuid BINARY(16))
RETURNS VARCHAR(36)
RETURN LOWER(CONCAT(
SUBSTR(HEX(uuid), 1, 8), '-',
SUBSTR(HEX(uuid), 9, 4), '-',
SUBSTR(HEX(uuid), 13, 4), '-',
SUBSTR(HEX(uuid), 17, 4), '-',
SUBSTR(HEX(uuid), 21)
));
And then simply use it in your queries:
SELECT
uuid_of(id)
name,
age
FROM users
And it produces:
(c6f5703b-fec2-43fd-8f45-45f06583d450, Some name, 20)
If you are looking for the opposite, i.e., how to convert from string to binary, perhaps to do a join or something, this is covered here : Convert UUID to/from binary in Node
This piece of SQL run on Mysql 5.7 helped lock in the concept for me:
SELECT
LOWER(CONCAT(
SUBSTR(HEX(UNHEX(REPLACE('43d597d7-2323-325a-90fc-21fa5947b9f3', '-', ''))), 1, 8), '-',
SUBSTR(HEX(UNHEX(REPLACE('43d597d7-2323-325a-90fc-21fa5947b9f3', '-', ''))), 9, 4), '-',
SUBSTR(HEX(UNHEX(REPLACE('43d597d7-2323-325a-90fc-21fa5947b9f3', '-', ''))), 13, 4), '-',
SUBSTR(HEX(UNHEX(REPLACE('43d597d7-2323-325a-90fc-21fa5947b9f3', '-', ''))), 17, 4), '-',
SUBSTR(HEX(UNHEX(REPLACE('43d597d7-2323-325a-90fc-21fa5947b9f3', '-', ''))), 21)
))
The output is 43d597d7-2323-325a-90fc-21fa5947b9f3.
string -> binary
So UNHEX(REPLACE('43d597d7-2323-325a-90fc-21fa5947b9f3', '-', '')) to convert a UUID to binary during an INSERT / UPDATE / JOIN / SELECT whatever, and
binary -> string
LOWER(CONCAT(
SUBSTR(HEX(uuid), 1, 8), '-',
SUBSTR(HEX(uuid), 9, 4), '-',
SUBSTR(HEX(uuid), 13, 4), '-',
SUBSTR(HEX(uuid), 17, 4), '-',
SUBSTR(HEX(uuid), 21)
))
The correct result is generated by the script below, the other scrips generated a UUID however not the right one.
CONCAT(
substr(hex(Id), 7, 2), substr(hex(Id), 5, 2), substr(hex(Id), 3, 2), substr(hex(Id), 1, 2), '-'
, substr(hex(Id), 11, 2) , substr(hex(Id), 9, 2) , '-'
, substr(hex(Id), 15, 2) , substr(hex(Id), 13, 2) , '-'
, substr(hex(Id), 17, 4) , '-'
, substr(hex(Id), 21, 12)
)
Results running the other scripts generated wrong UUID as per below:
Expected UUID - 2e9660c2-1e51-4b9e-9a86-6db1a2770422
What was generated - c260962e-511e-9e4b-9a86-6db1a2770422
As you can see they are different.
Here's an alternative using concat_ws
Store raw uuid in a variable #x
SELECT #x := hex(uuid)
FROM foo;
Use CONCAT_WS and SUBSTR to parse human readable UUID
SELECT
LOWER(CONCAT_WS('-',
SUBSTR(#x, 1, 8),
SUBSTR(#x, 9, 4),
SUBSTR(#x, 13, 4),
SUBSTR(#x, 17, 4),
SUBSTR(#x, 21)
)) AS uuid;
According to this Jira ticket https://jira.mariadb.org/browse/MDEV-15854 UUID_TO_BIN and BIN_TO_UUID did not make into the Mariadb Server release 10.5. If you are using this version and under of Mariadb Server you will have to use a custom implementation mentioned above.
Related
I am trying to use mysql to solve the question below.
Any idea how should I make it work? Thank you.
Tried to use the code below but extracted duplicate strings in two columns and it's hard-code so it's not working..
SELECT itemid, SUBSTRING_INDEX(SUBSTRING_INDEX(item_variation, ',', 1), ',', -1) 'type one',
SUBSTRING_INDEX(SUBSTRING_INDEX(item_variation, ',', 2), ',', -1) 'type two',
SUBSTRING_INDEX(SUBSTRING_INDEX(item_variation, ',', 3), ',', -1) 'type three',
SUBSTRING_INDEX(SUBSTRING_INDEX(item_variation, ',', 4), ',', -1) 'type four'
FROM data
Question:
To extract items with more than 3 types
|itemid|shopid|item_name|item_type|price|stock|creation_date|
|1|10000|clothes|{}|5|100|27/1/2018|
|2|10000|dress|{Pink: 20, Black: 20, Grey: 20}|20|100|20/2/2018|
|3|10001|t-shirt|{S: 2, M: 2, L: 2, XL: 2}|2|50|1/1/2018|
|4|10002|socks|{us5.5: 1, us9: 1, us4.5: 1, us10: 1, us7: 1, us6: 1, us5: 1}|1|1000|4/1/2018|
|5|10002|Gloves|{S: 2, M: 2}|2|500|6/1/2018|
Expected result
|itemid |item_name |item_type|
|3 |t-shirt |{S: 2, M: 2, L: 2, XL: 2}|
|4 |socks |{us5.5: 1, us9: 1, us4.5: 1, us10: 1, us7: 1, us6: 1, us5: 1}|
This ought to do:
select itemid, item_name, item_type
from t
where length(item_type) - length(replace(item_type, ',', '')) >= 3;
You need a special case to tell 0 and 1 apart. It would not work if item_type contains ',' in either key or value of the json-like field (missing quoted around strings to be json).
You just need to count number of comma(,)>=3. Try below code:
SELECT
Itemid,Item_Name,Item_type
FROM myjson
where ROUND (
(
LENGTH(item_type)
- LENGTH( REPLACE ( item_type, ",", "") )
) / LENGTH(",")
)>=3
I would like to pass the uuid of version varbinary(16) to a user-defined function in MySQL version 5.7.x - It throws an error
1406 - Data too long for column
I have a table with uuid primary key of type varbinary(16) - I would like to convert the binary(16) to human readable version so I written a function to convert the binary version.
CREATE DEFINER=`bala`#`localhost`
FUNCTION `bin_to_uuid`(`uuid` BINARY(32))
RETURNS varchar(32) CHARSET latin1
NO SQL
return LOWER(CONCAT(
SUBSTR(HEX(uuid), 1, 8), '-',
SUBSTR(HEX(uuid), 9, 4), '-',
SUBSTR(HEX(uuid), 13, 4), '-',
SUBSTR(HEX(uuid), 17, 4), '-',
SUBSTR(HEX(uuid), 21)
))
SELECT bin_to_uuid((UNHEX(REPLACE(uuid(), "-",""))))
MySQL version 5.7.x
The return type is varchar(32), but with the hyphens added, the text you're returning is 36 chars long.
Change the return type to varchar(36).
I have month value like "22018" in my column I need it like Feb-2018 in mysql workbench
You need to first extract the month from the date (considering it will have one or two digits), e.g.:
SELECT LPAD(SUBSTRING('22018', 1, LENGTH('22018') - 4), 2, '0');
This will give you 02. Now, you can extract the year with similar logic, e.g.:
SELECT SUBSTRING('22018', LENGTH('22018') - 4 + 1, LENGTH('22018'));
Finally, you can concatenate all these to get a string like 2018-02-01:
SELECT CONCAT(SUBSTRING('22018', LENGTH('22018') - 4 + 1, LENGTH('22018')),
'-',
LPAD(SUBSTRING('22018', 1, LENGTH('22018') - 4), 2, '0'), '-01');
Once this is done, you can use DATE_FORMAT function to get the required output:
SELECT DATE_FORMAT(CONCAT(SUBSTRING('22018', LENGTH('22018') - 4 + 1,
LENGTH('22018')),
'-',
LPAD(SUBSTRING('22018', 1, LENGTH('22018') - 4), 2, '0'), '-01'), '%M-%Y');
This is a real hair puller so any help, much appreciated!
I want to be able to determine the:
First day of the current custom/financial month
Last day of the current custom/financial month
And use these new columns Start_Date and end_Date as between Filters in the Matrix.
Note: I understand that if this was calendar Month, then that will be "quite" straightforward.
But in this case its quite different.
Please see image which might help with the context i am trying to work with:
I'm sure this is possible using expressions in SSRS but I don;t have time to investigate. In case it's useful, here's how I would do it in SQL.
Again there is probably a more elegant solution but this was what came to me.
I'll reproduced your data plus a few more dates either end for testing which I guessed based on your sample.
DECLARE #t TABLE(Custom_Date date, Custom_Day int)
INSERT INTO #t VALUES
('2017-10-26', 28),
('2017-10-27', 29),
('2017-10-28', 30),
('2017-10-29', 1),
('2017-10-30', 2),
('2017-10-31', 3),
('2017-11-01', 4),
('2017-11-02', 5),
('2017-11-03', 6),
('2017-11-04', 7),
('2017-11-05', 8),
('2017-11-06', 9),
('2017-11-07', 10),
('2017-11-08', 11),
('2017-11-09', 12),
('2017-11-10', 13),
('2017-11-11', 14),
('2017-11-12', 15),
('2017-11-13', 16),
('2017-11-14', 17),
('2017-11-15', 18),
('2017-11-16', 19),
('2017-11-17', 20),
('2017-11-18', 21),
('2017-11-19', 22),
('2017-11-20', 23),
('2017-11-21', 24),
('2017-11-22', 25),
('2017-11-23', 26),
('2017-11-24', 27),
('2017-11-25', 28),
('2017-11-26', 1),
('2017-11-27', 2),
('2017-11-28', 3)
Then two queries to pull out the correct dates which you could combine if required.
SELECT MAX(Custom_Date) FROM #t WHERE Custom_Date < getdate() AND custom_day = 1
SELECT MAX(Custom_Date)
FROM #t
WHERE
Custom_Date > getdate()
AND DATEDIFF(d, getdate(), Custom_Date)<=31
AND Custom_Day = (
SELECT MAX(custom_day)
FROM #t
WHERE
Custom_Date > getdate()
AND datediff(d, getdate(), Custom_Date)<=31
)
FYI: This would be a lot easier if you had a custom month/period and year in your dates table as then you could just look custom_day 1 and max(custom_day) where the custom month and year are the same as the current date.
Have teh follwoing query which is fine and dandy...
SELECT
`node`.`site_pages_id` AS `page_id` ,
GROUP_CONCAT(`parent`.`site_pages_id` SEPARATOR '/') AS `path` ,
IFNULL( CONCAT( '/' , GROUP_CONCAT(`parent`.`url` SEPARATOR '/') ) , '/' ) AS `url`
FROM
`site_pages` AS `node`
LEFT JOIN
`site_pages` AS `parent`
ON
`node`.`lft_limit` BETWEEN `parent`.`lft_limit` AND `parent`.`rgt_limit`
GROUP BY
`node`.`site_pages_id`
ORDER BY
`node`.`lft_limit` ASC
produces the following
1, 1, /
2, 1/2, /about-us
8, 1/2/8, /about-us/meet-the-team
3, 1/3, /web
5, 1/5, /print
6, 1/6, /branding
7, 1/7, /contact-us
All sweet.
I'd like to select the immediate parent and just the immediate parent for each node.
My feeble attempts at doing so without a nasty subquery fail misearably (not much better with TBO).
Tips muchly appreciated
No probs Martin...
page_id url lft_limit rgt_limit
1, NULL, 1, 14,
2, about-us, 2, 5,
3, web, 6, 7,
5, print, 8, 9,
6, branding, 10, 11,
7, contact-us, 12, 13,
8, meet-the-team, 3, 4,