How to convert binary to decimal(39,0) in MySQL? - mysql

I have ipv6 addresses stored in decimal(39,0) unsigned format.
I need to query the database. I'm using INET6_ATON() which converts ipv6 to binary. Now I need to convert that binary to decimal(39,0).
Is this possible in pure MySQL?

Yes, is possible. Because DECIMAL representation of IPv6 is bigger than maximum 64-bit integer (18,446,744,073,709,551,615), you must split the 'string' up to 16 characters and then to join them together:
example table:
mysql> select * from ip_table;
+----+-----------------------------------------+
| id | ipv6 |
+----+-----------------------------------------+
| 1 | 2001:db8:a0b:12f0::1 |
| 2 | 3731:54:65fe:2::a7 |
| 3 | FE80:0000:0000:0000:0202:B3FF:FE1E:8329 |
| 4 | FE80::0202:B3FF:FE1E:8329 |
+----+-----------------------------------------+
4 rows in set
SQL statement:
select id,
(
cast(conv(substr(HEX(INET6_ATON(t.`ipv6`)), 1, 16), 16, 10) as decimal(65))*18446744073709551616 +
cast(conv(substr(HEX(INET6_ATON(t.`ipv6`)), 17, 16), 16, 10) as decimal(65))
) as converted
from ip_table t
result:
+----+-----------------------------------------+
| id | converted |
+----+-----------------------------------------+
| 1 | 42540766414390830568948465903729639425 |
| 2 | 73361969000969283948743196392239923367 |
| 3 | 338288524927261089654163772891438416681 |
| 4 | 338288524927261089654163772891438416681 |
+----+-----------------------------------------+
4 rows in set
Note that last two IPs are the same, I've put them in different format in order to test is the script works properly.

Related

MySQL: Add Default Value to Joined Table when Row not Found

System info:
$ uname -srvm
Linux 5.15.0-56-generic #62-Ubuntu SMP Tue Nov 22 19:54:14 UTC 2022 x86_64
$ mysql --version
mysql Ver 8.0.31-0ubuntu0.22.04.1 for Linux on x86_64 ((Ubuntu))
I am very inexperienced with MySQL & have been looking for an answer to this for about half a week. I am working with two tables named character_stats & halloffame that I want to join in a query. They look like this:
mysql> SELECT name, level FROM character_stats;
+-----------+-------+
| name | level |
+-----------+-------+
| foo | 0 |
| bar | 0 |
| baz | 3 |
| tester | 4 |
| testertoo | 2 |
+-----------+-------+
mysql> SELECT * from halloffame;
+----+-----------+----------+--------+
| id | charname | fametype | points |
+----+-----------+----------+--------+
| 1 | bar | T | 0 |
| 2 | foo | T | 0 |
| 3 | baz | T | 0 |
| 4 | tester | T | 0 |
| 5 | testertoo | T | 0 |
| 6 | tester | D | 40 |
| 7 | tester | M | 92 |
| 8 | bar | M | 63 |
+----+-----------+----------+--------+
In my query, I want to display all the rows from character_stats & I want to join the points column from halloffame for fametype='M'. If there is no row for fametype='M', I want to set points to 0 for that character name, instead of omitting the entire row as is done in the following:
mysql> SELECT name, level, points FROM character_stats JOIN
-> (SELECT charname, points FROM halloffame WHERE fametype='M')
-> AS hof ON (hof.charname=name);
+--------+-------+--------+
| name | level | points |
+--------+-------+--------+
| tester | 4 | 92 |
| bar | 0 | 63 |
+--------+-------+--------+
So I want it to output this:
+-----------+-------+--------+
| name | level | points |
+-----------+-------+--------+
| foo | 0 | 0 |
| bar | 0 | 63 |
| baz | 3 | 0 |
| tester | 4 | 92 |
| testertoo | 2 | 0 |
+-----------+-------+--------+
I have tried to learn how to use IFNULL, IF-THEN-ELSE, CASE, COALESCE, & COUNT statements from what I have found in documentation & answers on stackoverflow.com. But as I said, I am very inexperienced & don't know how to implement them.
The following works on its own:
SELECT IFNULL((SELECT points FROM halloffame WHERE fametype='M'
AND charname='foo' LIMIT 1), 0) as points;
But I don't know how to join it to the character_stats table. The following would work if I knew how to get the value of character_stats.name before COALESCE is called:
SELECT name, level, 'M' AS fametype, points FROM character_stats
JOIN (SELECT COALESCE((SELECT points FROM halloffame WHERE
fametype='M' AND charname=name LIMIT 1), 0) AS points) AS hof;
According to Adding Default Values on Joining Tables I should be able to use CROSS JOIN, but I am doing something wrong as it still results in Unknown column 'cc.name' in 'where clause':
SELECT name, level, points FROM character_stats
CROSS JOIN (SELECT DISTINCT name FROM character_stats) AS cc
JOIN (SELECT COALESCE((SELECT points FROM halloffame WHERE
fametype='M' AND charname=cc.name LIMIT 1), 0) AS points) AS hof;
Some references I have looked at:
Returning a value even if no result
Usage of MySQL's "IF EXISTS"
Return Default value if no row found
MySQL.. Return '1' if a COUNT returns anything greater than 0
How do write IF ELSE statement in a MySQL query
Simple check for SELECT query empty result
Is there a function equivalent to the Oracle's NVL in MySQL?
MySQL: COALESCE within JOIN
Unknown Column In Where Clause With Join
Adding Default Values on Joining Tables
https://www.tutorialspoint.com/returning-a-value-even-if-there-is-no-result-in-a-mysql-query
I found that I can do the following:
SELECT name, level, COALESCE((SELECT points FROM
halloffame WHERE fametype='M' AND charname=name
LIMIT 1), 0) AS points FROM character_stats;
Though I would still like to know how to do it within a JOIN statement.

Find the max value in the last digit

I am using MySQL 5.5. I am facing a problem on how to find the max value in the last digit.
For example below the table, I want to get the max value is detected the last digit. The result should be 100-1-15
Table name: abc
+----+------------+
| id | code |
+----+------------+
| 1 | 100-1-1 |
| 2 | 100-1-2 |
| 3 | 100-1-15 |
| 4 | 100-1-6 |
| 5 | 100-1-3 |
| 6 | 100-1-5 |
| 7 | 100-1-9 |
+----+------------+
I am using below the SQL query, but doesn't work:
SELECT id,max(code) FROM abc;
Hope someone can guide me how to solve it and can get the max code is 100-1-15. Thanks.
SELECT *
from abc
order by SUBSTRING_INDEX(code, '-', -1) + 0 desc
limit 1
Try
Select
id,
code
from abc
order by max(CAST(SUBSTR(code, 7, LENGTH(code)-6) AS SIGNED))
limit 1;

Generate anonymous ids in JOIN query

The task is to extract customer and order data from a sensitive system. Data is stored in a MySQL database.
A customer can be associated with many orders. A simple LEFT JOIN gives me exactly what I require:
---------------------------------------------------------
| customer_id | order_id | order_quantity | order_value |
---------------------------------------------------------
| 1 | 100 | 3 | 100.00 |
| 1 | 105 | 12 | 400.00 |
| 2 | 103 | 2 | 75.00 |
---------------------------------------------------------
However, in the generated extract, I'm not allowed to reveal the customer_id nor the order_id. Instead, these ids need to be replaced by random, anonymized identifiers generated at the time of data export.
The relationship between customers and their orders still needs to be maintained in the resulting, extracted data export.
Desired outcome:
-------------------------------------------------------------------
| anon_customer_id | anon_order_id | order_quantity | order_value |
-------------------------------------------------------------------
| xyz | abc123 | 3 | 100.00 |
| xyz | def567 | 12 | 400.00 |
| pqr | hij890 | 2 | 75.00 |
-------------------------------------------------------------------
Is there a way to generate anon_customer_id and anon_order_id as part of the SELECT I'm running to build the data result?
One option would be using MySQL's native encryption methods like SHA1 or SHA2 and make a VIEW which you query and join with.
I've choosen to use SHA 512 because it has a very low probability different data could generate the same hash.
CREATE VIEW Table1_VIEW AS (
SELECT
<table>.*
, SHA2(<table>.customer_id, 512) AS anon_customer_id
, SHA2(<table>.order_id, 512) AS anon_order_id
FROM
<table>
)
Query and result
SELECT
*
FROM
Table1_VIEW
| customer_id | order_id | order_quantity | order_value | anon_customer_id | anon_order_id |
| ----------- | -------- | -------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| 1 | 100 | 3 | 100 | 4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed6fa46510a | 643c30f73a3017050b287794fc8c5bb9ab06b9ce38a1fc58df402a8b66ff58f69bf0a606ae17585352a0306f0e9752de8c5c064aed7003f52808b43ff992a603 |
| 1 | 105 | 12 | 400 | 4dff4ea340f0a823f15d3f4f01ab62eae0e5da579ccb851f8db9dfe84c58b2b37b89903a740e1ee172da793a6e79d560e5f7f9bd058a12a280433ed6fa46510a | 03d25c7071bce10d6b462d53854b969d9f61b982e3aee8771bdcca1ecb70495574e6929042f52e859ee9a253b58f776514180ff16e1338f5505e86c7ff328f72 |
| 2 | 103 | 2 | 75 | 40b244112641dd78dd4f93b6c9190dd46e0099194d5a44257b7efad6ef9ff4683da1eda0244448cb343aa688f5d3efd7314dafe580ac0bcbf115aeca9e8dc114 | 947de04bfae0bf062a66fc055d4c284c9779793d9bd58833ee7549fde1ff1effaf7aefdbc6c90ed0ac86c0acc82329e7c057d900c28ea7ed4724486f717ee38d |
demo
p.s You can also directly use SHA2() directly in a JOIN offcource.
Example Query
SELECT
table11.*
, SHA2(table11.customer_id, 512) AS anon_customer_id
, SHA2(table11.order_id, 512) AS anon_order_id
FROM
Table1 table11
LEFT JOIN
Table1 table12
ON
table11.customer_id = table12.customer_id
demo
MYSQL 5.7+ only
If you have atleast MySQL 5.7+ you have a even better option. Which is generated columns
CREATE TABLE Table1 (
`customer_id` INTEGER,
`order_id` INTEGER,
`order_quantity` INTEGER,
`order_value` INTEGER,
anon_customer_id VARCHAR(255) AS ( SHA2(Table1.customer_id, 512) ) VIRTUAL,
anon_order_id VARCHAR(255) AS ( SHA2(Table1.order_id, 512) ) VIRTUAL
);
demo
Edit because of the comment from Louis
my point was that someone will be able to extract the sensitive
customer id after it's hashed. Simply by calculating hashes of all
possible or likely customer id and seeing which is the same. If the
customer ID is not an increasing number with predictable range but
some randomly assigned very large number or indeed a long random
string it might be better
This is very true what you can do is add more entropy to the hash so the real id arn't that easy to bruteforce annymore.
In this case you add atleast 52 characters (datetime(6) and the reverse one) as entropy that should be more then enough to protect against bruteforces for (some) years to come.
CREATE VIEW Table1_VIEW_more_entropy AS (
SELECT
Table1.*
, SHA2(CONCAT_WS(':', Table1.id, Table1.date_created, REVERSE(Table1.date_created), Table1.customer_id), 512)
, SHA2(CONCAT_WS(':', Table1.id, Table1.date_created, REVERSE(Table1.date_created), Table1.order_id), 512)
FROM
Table1
);
see demo
You could use any hash function, for example, MD5:
SELECT MD5(customer_id) AS anon_customer_id FROM customers;
But be aware though, MD5 is not very secure: https://security.stackexchange.com/questions/19906/is-md5-considered-insecure

MYSQL - Count items separated by comma or not

I have a tinytext field which can contain 3 differents value formatted as followed:
NULL
"X" or "Y" (where X and Y can be any number)
"A,B,C,D" (where A, B, C and D can be any number)
I want to query the table and count the number of items separated or not with a comma.
For example with these lines:
42
NULL
42,56,99
24,10090
Then the expected count would be 6.
I can't find the correct query.
Okay here's the test data:
mysql> create table t (f tinytext);
mysql> insert into t values ('42'), (null), ('42,56,99'), ('24,10090');
mysql> select * from t;
+----------+
| f |
+----------+
| 42 |
| NULL |
| 42,56,99 |
| 24,10090 |
+----------+
You can calculate how many numbers in the string as the difference in the length of the string and the string with commas removed (add 1 for the first number in the list).
mysql> select f, length(f), length(replace(f,',','')), 1+ length(f)-length(replace(f,',','')) from t;
+----------+-----------+---------------------------+----------------------------------------+
| f | length(f) | length(replace(f,',','')) | 1+ length(f)-length(replace(f,',','')) |
+----------+-----------+---------------------------+----------------------------------------+
| 42 | 2 | 2 | 1 |
| NULL | NULL | NULL | NULL |
| 42,56,99 | 8 | 6 | 3 |
| 24,10090 | 8 | 7 | 2 |
+----------+-----------+---------------------------+----------------------------------------+
So then use SUM() to get the total. SUM() ignores NULLs.
mysql> select sum(1+length(f)-length(replace(f,',',''))) from t;
+--------------------------------------------+
| sum(1+length(f)-length(replace(f,',',''))) |
+--------------------------------------------+
| 6 |
+--------------------------------------------+
This would be easier if you don't store comma-separated lists in a string.

Get one row in table1 and Two or more rows in table2 in one array

Im newbie here. Suppose 2 tables in database:
Table 1: tournaments
+------------+----------+---------+----------+
| id | format | Size | host |
+------------+----------+---------+----------+
| 7 | Single | 9 | Daniel |
| 8 | Single | 4 | Oscar |
+------------+----------+---------+----------+
Table 2: matchs
+------------+----------+-------------+----------+----------+
| id | player | position | score | winner |
+------------+----------+-------------+----------+----------+
| 7 | Arturo | 0 | 3 | 1 |
| 7 | Pablo | 1 | 2 | 0 |
| 8 | Ale | 0 | 1 | 0 |
| 8 | Maria | 1 | 5 | 1 |
+------------+----------+-------------+----------+----------+
Now i want to get the host, size, winner, player, Score where ID is 7 and 8. Do you know how iget the following structure?:
[ Host:Daniel, size: 9, [ winner:1, player: Arturo, Score: 3], [winner:0, player: Pablo, Score: 2]], [host:Óscar, size:4,[winner:0, player: Ale, Score:0],....
Thanks in advance :)
Use JOIN Syntax to join the tables by id field:
SELECT
host, size, winner, player, score
FROM
tournaments INNER JOIN matchs USING (id);
If you want to select rows that contains specific idonly, then use WHERE clause. In your case append following to the query before semicolon:
WHERE `id` IN (7, 8)
Note, the MySQL can not create complex objects. SELECT returns set of rows only. To convert returned data set to structure, use external tools of your server side platform: C, C++, C#, VB.NET, Python, PHP, etc.