In this case, why '--+' the comment style of mysql can work? - mysql

The example below comes from sqli-lab. In MySQL's doc(comment), the "-- " (double-dash followed by at least one whitespace) means a line's comment. It does work in some situation actually.
My question is how it work in the example here, why it can list all records of the 'users' table. Can you give some ideas about its mechanism? Thx!
mysql> select username, password from users where username = '' --+ '';
+----------+------------+
| username | password |
+----------+------------+
| Dumb | Dumb |
| Angelina | I-kill-you |
| Dummy | p#ssword |
| secure | crappy |
| stupid | stupidity |
| superman | genious |
| batman | mob!le |
| admin | admin |
| admin1 | admin1 |
| admin2 | admin2 |
| admin3 | admin3 |
| dhakkan | dumbo |
| admin4 | admin4 |
+----------+------------+

Actually #scaisEdge is right, the '--+' in MySQL(interactive cmdline) is NOT a comment, this usage is usually used for URLencoding(a space in a query part may be encoded to '+' or '%20').
In this case, '--+' is just 2 types of operators: plus&minus, and one - and one + offset. So this sequence is equal to:
select username, password from users where username = '' - '';
Original:
mysql> select username, password from users where username = '' --+ '';
+----------+------------+
| username | password |
+----------+------------+
| Dumb | Dumb |
| Angelina | I-kill-you |
| Dummy | p#ssword |
| secure | crappy |
...
Now:
mysql> select username, password from users where username = '' - '';
+----------+------------+
| username | password |
+----------+------------+
| Dumb | Dumb |
| Angelina | I-kill-you |
| Dummy | p#ssword |
| secure | crappy |
...
You can see the results are same.
Secondly, '' equal INTEGER 0 here.
In MySQL, any field without a valid integer will equate to 0.
mysql> select '' = 0;
+--------+
| '' = 0 |
+--------+
| 1 |
+--------+
mysql> select '0s28' = 0;
+------------+
| '0s28' = 0 |
+------------+
| 1 |
+------------+
mysql> select '8s28' = 0;
+------------+
| '8s28' = 0 |
+------------+
| 0 |
+------------+
mysql> select '8s28' = 8;
+------------+
| '8s28' = 8 |
+------------+
| 1 |
+------------+
====Type Conversion====
mysql> select '12s' + 3;
+-----------+
| '12s' + 3 |
+-----------+
| 15 |
+-----------+
mysql> select 's52s6' + 3;
+-------------+
| 's52s6' + 3 |
+-------------+
| 3 |
+-------------+
mysql> select 's8' + 3;
+----------+
| 's8' + 3 |
+----------+
| 3 |
+----------+
So '' - '' means 0 - 0 is still 0.
While the column USERNAME doesn't has one name that starts with a valid numeric character(not 0), so all the name equal 0 and match the conditon 'where username = 0'
mysql> select 'Dumb' = 0;
+------------+
| 'Dumb' = 0 |
+------------+
| 1 |
+------------+
To verify this conclusion, we can insert into a record that username starts with a integer like '4love'. You will see all records are listed except the new one.
A similar question is here:
mySQL returns all rows when field=0

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 How to convert the column of mysql select result to row

I have a mysql tableA, it changes it's fieldname every minute,i need it's fieldname,
so i do like this:
select COLUMN_NAME from information_schema.COLUMNS where table_name = 'tableA';
output:
+-------------+
| COLUMN_NAME |
+-------------+
| min |
| unkonwn1 |
| unkonwn2 |
| unkonwn3 |
| unkonwn4 |
| unkonwn5 |
| average |
+-------------+
but i need select output like this:
+-------+----------+----------+---------+------------+---------+---------+
| min | unkonwn1 | unkonwn2 | unkonwn3| unkonwn4 | unkonwn5| average |
+-------+----------+----------+---------+------------+---------+---------+
how to get this kind of result ?

Find table name on the database using stored procedure on MySql 8.0.17

This is a list name of tables stored a database MySql version 8.0.17
+------------------+
| listTable |
+------------------+
| Table_A2_11_2021 |
| Table_L7_12_2021 |
| Table_C3_1_2021 |
| Table_D8_10_2021 |
| Table_T0_11_2021 |
| Table_E9_3_2021 |
| Table_L4_2_2021 |
| Table_O1_12_2021 |
| Table_P2_5_2021 |
| Table_Q2_10_2021 |
| Table_A3_12_2021 |
| Table_S5_9_2021 |
| Table_T8_11_2021 |
| Table_Q6_1_2021 |
+------------------+
The table name storage policy is
Table_
Western alphabet letter (issued by an algorithm that recognizes the connected user, privileges, etc.)_
Random number_
Month Number_
Current Year
I need find on the database MySql all the table for first Western alphabet letter without Random_number for this return
+-----------------+
| listTable |
+-----------------+
| Table_A_11_2021 |
| Table_L_12_2021 |
| Table_C_1_2021 |
| Table_D_10_2021 |
| Table_T_11_2021 |
| Table_E_3_2021 |
| Table_L_2_2021 |
| Table_O_12_2021 |
| Table_P_5_2021 |
| Table_Q_10_2021 |
| Table_A_12_2021 |
| Table_S_9_2021 |
| Table_T_11_2021 |
| Table_Q_1_2021 |
+-----------------+
I have idea to use this Stored Procedure below but I just can't extract first Western alphabet letter without Random number.
CREATE DEFINER=`root`#`%` PROCEDURE `SP_SIX_MONTHS`()
BEGIN
DECLARE tyear INT(4);
DECLARE tmonth INT(2);
SET tyear = YEAR(CURDATE());
SET tmonth = MONTH(DATE_SUB(CURDATE(),INTERVAL 6 MONTH));
SET #s = CONCAT('SELECT
FROM information_schema.TABLES
WHERE table_name LIKE ''table#_',???,'%#_',tmonth,'#_',tyear,''' ESCAPE ''#'';');
PREPARE stmt FROM #s;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
UPDATE
return of suggestion
+------------+
| t |
+------------+
| Table_A_13 |
| Table_C_12 |
| Table_D_1 |
| Table_E_5 |
| Table_L_12 |
| Table_O_8 |
| Table_P_12 |
| Table_Q_6 |
| Table_S_14 |
| Table_T_4 |
+------------+
10 rows in set (0.12 sec)
Hmmm . . . I think this actually does what you want:
select min(listtable)
from t
group by substr(listtable, 7, 1);
This interprets "first" as "first alphabetically".
If you don't want the digit, you can remove that:
select min(insert(listtable, 8, 1, ''))
from t
group by substr(listtable, 7, 1);
Here is a db<>fiddle.

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 - select distinct mutually exclusive (based on another column's value) rows

First off, I would like to say that if after reading the question, anyone has a suggestion on a more informative title for this question, please tell me, as I think mine is somewhat lacking, now, on to business...
Given this table structure:
+---------+-------------------------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+-------------------------------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| account | varchar(20) | YES | UNI | NULL | |
| domain | varchar(100) | YES | | NULL | |
| status | enum('FAILED','PENDING','COMPLETE') | YES | | NULL | |
+---------+-------------------------------------+------+-----+---------+----------------+
And this data:
+----+---------+------------------+----------+
| id | account | domain | status |
+----+---------+------------------+----------+
| 1 | jim | somedomain.com | COMPLETE |
| 2 | bob | somedomain.com | COMPLETE |
| 3 | joe | somedomain.com | COMPLETE |
| 4 | frank | otherdomain.com | COMPLETE |
| 5 | betty | otherdomain.com | PENDING |
| 6 | shirley | otherdomain.com | FAILED |
| 7 | tom | thirddomain.com | FAILED |
| 8 | lou | fourthdomain.com | COMPLETE |
+----+---------+------------------+----------+
I would like to select all domains which have a 'COMPLETE' status for all accounts (rows).
Any domains which have a row containing any value other then 'COMPLETE' for the status must not be returned.
So in the above example, My expected result would be:
+------------------+
| domain |
+------------------+
| somedomain.com |
| fourthdomain.com |
+------------------+
Obviously, I can achieve this by using a sub-query such as:
mysql> select distinct domain from test_table where status = 'complete' and domain not in (select distinct domain from test_table where status != 'complete');
+------------------+
| domain |
+------------------+
| somedomain.com |
| fourthdomain.com |
+------------------+
2 rows in set (0.00 sec)
This will work fine on our little mock-up test table, but in the real situation, the tables in question will be tens (or even hundreds) of thousands of rows, and I'm curious if there is some more efficient way to do this, as the sub-query is slow and intensive.
How about this:
select domain
from test_table
group by domain
having sum(case when status = 'COMPLETE'
then 0 else 1 end) = 0
I think this will work. Effectively just joins two basic queries together, then compares their count.
select
main.domain
from
your_table main
inner join
(
select
domain, count(id) as cnt
from
your_table
where
status = 'complete'
group by
domain
) complete
on complete.domain = main.domain
group by
main.domain
having
count(main.id) = complete.cnt
You should also ensure you have an index on domain as this relies on a join on that column.