How to get just a subset of a blob - mysql

The same question has been asked here: MySQL blob: how to get just a subset of the stored data.
However, the provided solution does not work, neither SUBSTRING nor MID are working properly with BLOB datatypes. Did I miss something with this query: SELECT SUBSTRING(file, 12, 48) FROM log WHERE id=8, The subset starts at position 1 and the length is somewhat to 48.
Thank you.

The SUBSTRING function seems to work fine for me on VARBINARY and BLOB, at least in MariaDB 5.6.20:
MySQL [gps]> show create table KV \G
*************************** 1. row ***************************
Table: KV
Create Table: CREATE TABLE `KV` (
`kv_key` varbinary(767) NOT NULL,
`kv_value` longblob NOT NULL,
PRIMARY KEY (`kv_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin
1 row in set (0.00 sec)
MySQL [gps]> select HEX(kv_key), HEX(kv_value) from KV where kv_key > x'99' limit 10;
+--------------------------------------------------------+------------------------+
| HEX(kv_key) | HEX(kv_value) |
+--------------------------------------------------------+------------------------+
| FC0696FC1A4AD423B65465FC3D7BBBAFCBDCD8FC657A8BB203FE0F | |
| FC114A8CF4C01BD7 | 01FE589B5E0200 |
| FC114A8CF4C01BD7FCC991 | 55414200 |
| FC114AF271B994BF | 01FE589B5E0200 |
| FC114AF271B994BFFCC991 | 434F50442050696C6F7400 |
| FC1A4AD423B65465 | 01FE589B5E0200 |
| FC1A4AD423B65465FC2550FCB7D4D490FCA2E4 | |
+--------------------------------------------------------+------------------------+
10 rows in set (0.00 sec)
MySQL [gps]> select HEX(SUBSTRING(kv_key, 4, 4)), HEX(kv_value) from KV where kv_key > x'99' limit 10;
+------------------------------+------------------------+
| HEX(SUBSTRING(kv_key, 4, 4)) | HEX(kv_value) |
+------------------------------+------------------------+
| FC1A4AD4 | |
| 8CF4C01B | 01FE589B5E0200 |
| 8CF4C01B | 55414200 |
| F271B994 | 01FE589B5E0200 |
| F271B994 | 434F50442050696C6F7400 |
| D423B654 | 01FE589B5E0200 |
| D423B654 | |
+------------------------------+------------------------+
10 rows in set (0.01 sec)
MySQL [gps]> select HEX(kv_key), HEX(SUBSTRING(kv_value, 4, 4)) from KV where kv_key > x'99' limit 10;
+--------------------------------------------------------+--------------------------------+
| HEX(kv_key) | HEX(SUBSTRING(kv_value, 4, 4)) |
+--------------------------------------------------------+--------------------------------+
| FC0696FC1A4AD423B65465FC3D7BBBAFCBDCD8FC657A8BB203FE0F | |
| FC114A8CF4C01BD7 | 9B5E0200 |
| FC114A8CF4C01BD7FCC991 | 00 |
| FC114AF271B994BF | 9B5E0200 |
| FC114AF271B994BFFCC991 | 44205069 |
| FC1A4AD423B65465 | 9B5E0200 |
| FC1A4AD423B65465FC2550FCB7D4D490FCA2E4 | |
+--------------------------------------------------------+--------------------------------+
10 rows in set (0.00 sec)

Related

How to get no of bytes occupied by a column in MySQL?

I need to know how many bytes are occupied by a column in MySQL.
Consider the following schema -
+-------+---------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------------------+------+-----+---------+-------+
| id | bigint(20) unsigned | NO | PRI | NULL | |
| data | longblob | YES | | NULL | |
+-------+---------------------+------+-----+---------+-------+
Ignore id field, lets talk about data field. Consider these tuples -
+-------+---------------------+
| id | data |
+-------+---------------------+
| 1 | {ab₹} |
| 2 | {ab} |
+-------+---------------------+
So, what I need is size in bytes not no of characters like-
+---------------------+
| size_in_bytes |
+---------------------+
| 7 | // {->(1), a->(1), b->(1), ₹->(3), }->(1)
| 4 | // {->(1), a->(1), b->(1), }->(1)
+---------------------+
After hours of search I found few functions which only result in no of characters.
select OCTET_LENGTH(data) from table_name;
+--------------------+
| OCTET_LENGTH(data) |
+--------------------+
| 5 |
| 4 |
+--------------------+
SELECT LENGTH(data) from table_name;
+--------------+
| LENGTH(data) |
+--------------+
| 5 |
| 4 |
+--------------+
SELECT char_length(data) from table_name;
+-------------------+
| char_length(data) |
+-------------------+
| 5 |
| 4 |
+-------------------+
Similar Question -> How to get size of column in mysql table but none of the answers results in bytes.
How to get the sizes of the tables of a MySQL database? and this is for size of the table.
MySQL version -> 8.0
Count bits
select BIT_LENGTH (N'{ab₹}')/8;
returns 7.0000
db<>fiddle

MYSQL filled some rows, but was't suppost to

So I made a basic struct of a database, and mysql filled some rows on a table and I don't know why.
Maybe I can explain myself with the tables
mysql> show fields
-> from products;
+--------------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+--------------------+--------------+------+-----+---------+-------+
| ID_PRODUCT | decimal(8,0) | NO | PRI | NULL | |
| ID_CATEGORY | decimal(8,0) | NO | MUL | NULL | |
| NAME | text | NO | | NULL | |
| BRAND | text | NO | | NULL | |
| PICTURE | longblob | YES | | NULL | |
| OBSERVATIONS | text | YES | | NULL | |
| QUANT_UNIT | text | NO | | NULL | |
+--------------------+--------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
So this is a table with products, and I have one other table with the prices so I can save the history of prices
mysql> show fields from prices;
+------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+-------+
| ID_PRODUCT | decimal(8,0) | NO | MUL | NULL | |
| PRICE | text | NO | | NULL | |
| TIME | timestamp | NO | | NULL | |
+------------+--------------+------+-----+---------+-------+
3 rows in set (0.00 sec)
And if I insert the values into the product and prices tables
mysql> insert into products (id_product, id_category, name, brand, quant_unit)
-> values (1, 0, 'rice', 'XPTO', 'kg');
Query OK, 1 row affected (0.01 sec)
mysql> select * from products;
+------------+--------------+--------+----------+----------------+-------------+--------------------+
| ID_PRODUCT | ID_CATEGORY | NAME | BRAND | PICTURE | OBSERVATION | QUANT_UNIT |
+------------+--------------+--------+----------+----------------+-------------+--------------------+
| 0 | 0 | mug | no_brand | 0x | favorite | un |
| 1 | 0 | rice | xpto | 0x | NULL | kg |
+------------+--------------+--------+----------+----------------+-------------+--------------------+
2 rows in set (0.00 sec)
mysql> insert into prices (id_product, price, time)
-> values (0, '5', CURRENT_TIMESTAMP);
Query OK, 1 row affected (0.01 sec)
mysql> select * from prices;
+------------+-------+---------------------+
| ID_PRODUCT | PRICE | TIME |
+------------+-------+---------------------+
| 0 | 7.30 | 2020-11-27 23:25:34 |
| 1 | 5 | 2020-11-27 23:29:12 |
+------------+-------+---------------------+
2 rows in set (0.00 sec)
all fine so far, but if I do a select with both of the tables
mysql> select pro.id_product, pro.id_category, pro.name, pro.brand, pro.quant_unit, pre.price
-> from products pro, prices pri;
+------------+--------------+--------+----------+-------------+-------+
| id_product | id_category | name | brand | quant_unit | price |
+------------+--------------+--------+----------+-------------+-------+
| 0 | 0 | mug | no_brand | un | 7.30 |
| 1 | 0 | rice | xpto | kg | 7.30 |
| 0 | 0 | mug | no_brand | un | 5 |
| 1 | 0 | rice | xpto | kg | 5 |
+------------+--------------+--------+----------+-------------+-------+
4 rows in set (0.00 sec)
those are my constraints to connect the tables
alter table PRICES add constraint FK_PRICES_COST_PRODUCTS foreign key (ID_PRODUCT)
references PRODUCTS (ID_PRODUCT) on delete restrict on update restrict;
alter table PRODUCTS add constraint FK_PRODUCTS_PERTENCE_CATEGORY foreign key (ID_CATEGORY)
references CATEGORY (ID_CATEGORY) on delete restrict on update restrict;
I can't even search the problem because it doesn't make sense to me... But I'm new at SQL, so I think I'm doing something wrong...
I will appreciate any feedback, thanks!
I advise against using comma-join but in your query you didn't specify what to match between those two tables. You can simply fix that by adding WHERE in your query like this:
select pro.id_product, pro.id_category, pro.name, pro.brand, pro.quant_unit, pre.price
from products pro, prices pri
where pro.id_product=pri.id_product;
But nowadays, most people uses JOIN instead of comma-join, hence:
select pro.id_product, pro.id_category, pro.name, pro.brand, pro.quant_unit, pre.price
from products pro JOIN prices pri
ON pro.id_product=pri.id_product;
There's a lot of JOIN types in MySQL and you can refer to this documentation.

Mysql: How to create a column which is the difference between a column in a Table & another column in a View

In the database 'college2' there are 3 TABLES:'student, course & enrolment', and one(1) VIEW:'enrolment_status', which is created using the following command:
CREATE VIEW enrolment_status AS
SELECT code, COUNT(id)
FROM enrolment
GROUP BY code;
Explain command for 'course,enrolment and enrolment_status' results in:
mysql> EXPLAIN course;
+---------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+-------------+------+-----+---------+-------+
| code | char(8) | NO | PRI | NULL | |
| name | varchar(90) | YES | MUL | NULL | |
| max_enrolment | char(2) | YES | | NULL | |
+---------------+-------------+------+-----+---------+-------+
3 rows in set (0.09 sec)
mysql> explain enrolment;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| id | char(6) | YES | MUL | NULL | |
| code | char(8) | YES | MUL | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.02 sec)
mysql> explain enrolment_status;
+-----------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------+------------+------+-----+---------+-------+
| code | char(8) | YES | | NULL | |
| COUNT(id) | bigint(21) | NO | | 0 | |
+-----------+------------+------+-----+---------+-------+
2 rows in set (0.18 sec)
'max_enrolment' column in 'course' TABLE is the maximum allowed # of student for each course, say 10 or 20.
'count(id)' column in 'enrolment_status' VIEW (not table) is actual # of students enrolled in each course.
'id' column in 'enrolment' TABLE is the student id enrolled in a course.
HERE'S MY QUESTION:
I want to have the '# of seats left' which is the difference between 'max_enrolment' column and 'count(id)' column.
'#of seats left' can be a stand alone table or view or a column added to any of the above tables. How can i do this:
I tried many commands including the following,
CREATE VIEW seats_left AS (
SELECT course.code, course.max_enrolment - enrolment_status.count
FROM course, enrolment_status
WHERE course.code = enrolment_status.code);
...which gives me the following error message:
ERROR 1054 (42S22): Unknown column 'enrolment_status.count' in 'field list'
mysql> SELECT*FROM enrolment_status;
+----------+-----------+
| code | COUNT(id) |
+----------+-----------+
| COMP9583 | 7 |
| COMP9585 | 9 |
| COMP9586 | 7 |
| COMP9653 | 7 |
| COMP9654 | 7 |
| COMP9655 | 8 |
| COMP9658 | 7 |
+----------+-----------+
7 rows in set (0.00 sec)
mysql> SELECT code, max_enrolment FROM course;
+----------+---------------+
| code | max_enrolment |
+----------+---------------+
| COMP9583 | 10 |
| COMP9585 | 15 |
| COMP9586 | 15 |
| COMP9653 | 12 |
| COMP9654 | 10 |
| COMP9655 | 12 |
| COMP9658 | 12 |
+----------+---------------+
7 rows in set (0.00 sec)
+----------+---------------------+
| code | max_enrolment - cnt |
+----------+---------------------+
| COMP9583 | 9 |
| COMP9585 | 14 |
| COMP9586 | 14 |
| COMP9653 | 11 |
| COMP9654 | 9 |
| COMP9655 | 11 |
| COMP9658 | 11 |
+----------+---------------------+
7 rows in set (0.09 sec)
Try to use an acronym for in the view.
CREATE VIEW enrolment_status AS
SELECT code, COUNT(id) count
FROM enrolment
GROUP BY code;
Then you should be able to do this:
CREATE VIEW seats_left AS (
SELECT course.code, course.max_enrolment - enrolment_status.count
FROM course, enrolment_status
WHERE course.code = enrolment_status.code);
If you cannot change the view, then you must use the exact same name in the query:
CREATE VIEW seats_left AS (
SELECT course.code, course.max_enrolment - enrolment_status.'count(id)'
FROM course, enrolment_status
WHERE course.code = enrolment_status.code);
Try this:
SELECT b.`code`,max_enrolment - cnt from
(select `code`, cnt from
(select count(1) as cnt,`code` from enrolment_status
GROUP BY `code`) as a) as a
LEFT JOIN
(SELECT code,max_enrolment from course) as b
on a.`code` = b.`code`
You can change left join to right join

mysql INNER_JOIN subquery

I have inherited a MySQL database, that has a table as follows:
mysql> describe stock_groups;
+--------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| group | varchar(5) | YES | | NULL | |
| name | varchar(255) | YES | | NULL | |
| parent | varchar(5) | YES | | NULL | |
| order | int(11) | YES | | NULL | |
+--------+--------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
When I run mysql> select * from stock_groups wheregroup='D2';
I get:
mysql> select * from stock_groups where `group`='D2';
+----+-------+------+--------+-------+
| id | group | name | parent | order |
+----+-------+------+--------+-------+
| 79 | D2 | MENS | D | 51 |
+----+-------+------+--------+-------+
1 row in set (0.00 sec)
and also i have a table:
mysql> describe stock_groups_styles_map;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| group | varchar(5) | NO | | NULL | |
| style | varchar(25) | NO | | NULL | |
+-------+-------------+------+-----+---------+----------------+
3 rows in set (0.01 sec)
when I run:
mysql> select `group` from stock_groups_styles_map where style='N26';
+-------+
| group |
+-------+
| B1 |
| B11 |
| D2 |
| V2 |
+-------+
4 rows in set (0.00 sec)
how do i get the stock_groups.name ?
Join the tables, and select only the data you need. If you need unique rows, use the distinct keyword:
select -- If you need unique names, use "select distinct" instead of "select"
sg.name
from
stock_groups_styles_map as sgs
inner join stock_groups as sg on sgs.group = sg.group
where
sgs.style = 'N26'
You could also solve this using subqueries, but that would be rather inneficient in this case.
Something important
You should add the appropriate indexes to your tables. It will improve the performance of your database.
You can use inner join on group column
SELECT ss.group, sg.name from
stock_groups sg inner join stock_groups_styles_map ss on ss.group = sg.group
where ss.style='N26'
SELECT stock_groups.name FROM stock_groups_styles_map, stock_groups WHERE stock_groups_styles_map.style ='N26';
worked for me.

Convert lat/lng pairs using GeomFromText('POINT(1 1)') and insert in another column

In my previous question Search for range Latitude/Longitude coordinates My solution was to create the table below.
mysql> select * from spatial_table where MBRContains(GeomFromText('LINESTRING(9 9, 11 11)'), my_spots);
+------+---------------------------------+
| id | my_spots | my_polygons |
+------+-------------+-------------------+
| 1 | $# $# $# $# |
+------+-------------+-------------------+
Now I need to convert and move my existing lat/lng pairs in the table below to spatial_table. How would I structure my query to acheive this? I am currently using the queries below to insert.
mysql> insert into spatial_table values (1, GeomFromText('POINT(1 1)'), GeomFromText('POLYGON((1 1, 2 2, 0 2, 1 1))'));
Query OK, 1 row affected (0.00 sec)
mysql> insert into spatial_table values (1, GeomFromText('POINT(10 10)'), GeomFromText('POLYGON((10 10, 20 20, 0 20, 10 10))') );
Query OK, 1 row affected (0.00 sec)
Existing table:
+-------------+---------+--------+-----------+----- ------+-------------+--------------+
| location_id | country | region | city | latitude | longitude | name |
+=============|=========|========|===========|============|=============|==============|
| 316625 | US | CA | Santa Cruz| 37.044799 | -122.102096 | Rio Theatre |
+-------------+---------+--------+-----------+------------+-------------+--------------+
Here is the secret recipe to success :)
My original table:
mysql> describe gls;
+-------------+--------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+-------+
| location_id | int(255) | NO | PRI | 0 | |
| country | varchar(255) | NO | | | |
| region | varchar(255) | NO | | | |
| city | varchar(255) | NO | | | |
| latitude | float(13,10) | NO | | | |
| longitude | float(13,10) | NO | | | |
+-------------+--------------+------+-----+---------+-------+
8 rows in set (0.00 sec)
step 1: Add new POINT column
mysql> alter table gls add my_point point;
Query OK, 247748 rows affected (4.77 sec)
Records: 247748 Duplicates: 0 Warnings: 0
Step 2: Update my_point with values from lat/lng fields.
UPDATE gls SET my_point = PointFromText(CONCAT('POINT(',gls.longitude,' ',gls.latitude,')'));
Step 3: check
mysql> select aswkt(my_point) from gls where city='Santa Cruz';
+--------------------------------------+
| aswkt(my_point) |
+--------------------------------------+
| POINT(-122.1020965576 37.0447998047) |
| POINT(-66.25 -12.2833003998) |
| POINT(-2.3499999046 42.6666984558) |
+--------------------------------------+
Let's say you have a table like this:
mysql> select * from spatial_table;
+------+---------------------------+-----------------------------------------------------------------------------------+---------+--------+
| id | my_spots | my_polygons | lon | lat |
+------+---------------------------+-----------------------------------------------------------------------------------+---------+--------+
| 1 | ?? ?? | ?? ?? # # # ?? ?? | -122.11 | -37.11 |
| 1 | $# $# | $# $# 4# 4# 4# $# $# | -122.11 | -37.11 |
+------+---------------------------+-----------------------------------------------------------------------------------+---------+--------+
2 rows in set (0.00 sec)
If you want to make a geometry column with the lon lat values (as points only, syntax is a little different for other kinds of geometries), you can do this:
mysql> alter table spatial_table add column (go_slugs geometry);
This is a geometry type, if it is all single locations you could make the column type point. Then just update the new column:
mysql> update spatial_table set go_slugs = point(lon, lat);
Use the aswkt function to get human readable data to confirm this is correct:
mysql> select aswkt(go_slugs) from spatial_table;
+-----------------------------------------------+
| aswkt(go_slugs) |
+-----------------------------------------------+
| POINT(-122.11000061035156 -37.11000061035156) |
| POINT(-122.11000061035156 -37.11000061035156) |
| POINT(-123.4000015258789 37.79999923706055) |
+-----------------------------------------------+
3 rows in set (0.00 sec)