Using value in a variable? - mysql

I have the following code:
insert conversion (h1, g1, s1)
values(
'14',
(select g1 from conversion where h1 = '14'),
'15');
I have to create tons of inserts manually and only want use variable for the values - less typos that way.
So want to do something like:
val1 = '14'
val2 = '15'
insert conversion (h1, g1, s1)
values(
val1,
(select g1 from conversion where h1 = val1),
val2);
How can I do this?

You can do somethink like that:
insert conversion (h1, g1, s1)
(select '14',g1,'15' from conversion where h1 = 14)

From the posting above - this did the trick:
http://dev.mysql.com/doc/refman/5.0/en/user-variables.html

Related

Can't perform json operations in where clause on filtered and then casted text when a join is used for the filtering. Where clause executes too early

Postgres (12.2) Setup:
CREATE TABLE public.test_table (
id int NOT NULL,
value_type text NOT NULL,
value text NOT NULL
);
INSERT INTO public.test_table
(id, value_type, value)
VALUES (1, 'string', 'a'),
(2, 'json', '{"hello":"world"}'),
(3, 'json', '{"color":"blue"}');
Initial Queries:
select value::jsonb as json_value from test_table where value_type = 'json'
json_value |
------------------|
{"hello": "world"}|
{"color": "blue"} |
But I'm only interested in ones with 'color'.
Moving it to a subquery so that I can get only 'color', also just fine:
select only_json.json_value
from(
select value::jsonb as json_value from test_table where value_type = 'json'
) only_json
where only_json.json_value ? 'color' = true
json_value |
------------------|
{"color": "blue"} |
Now let's break that main table up into two, and suddenly effectively the same query has trouble:
CREATE TABLE public.test_table (
id INT PRIMARY KEY,
value TEXT NOT NULL
);
CREATE TABLE public.test_types (
id INT PRIMARY KEY REFERENCES public.test_table (id),
value_type TEXT NOT NULL
);
INSERT INTO public.test_table
(id, value)
VALUES (1, 'a'),
(2, '{"hello":"world"}'),
(3, '{"color":"blue"}');
insert into public.test_types
(id, value_type)
values (1, 'string'),
(2, 'json'),
(3, 'json');
Now this query:
select id, value from (
select id, value::jsonb from public.test_table natural join public.test_types
where value_type = 'json') only_json
returns, as expected:
id|value |
--|------------------|
2|{"hello": "world"}|
3|{"color": "blue"} |
But as soon as I attach the where clause, it fails:
select id, value from (
select id, value::jsonb from public.test_table natural join public.test_types
where value_type = 'json') only_json
where only_json.value ? 'color' = true
SQL Error [22P02]: ERROR: invalid input syntax for type json
Detail: Token "a" is invalid.
Where: JSON data, line 1: a
It's somehow resurrected the value of 'a' that was well-eliminated prior to this where clause. So what gives? Why does the join cause it to apply the last where clause (which should happen logically last) too early? Failed workarounds I've tried:
Using left join instead of natural join.
Applying where value_type = 'json' to the joined table first, prior to the join.
Moving it to a "with".
Creating a view and then applying the where clause to a select from the view.
Creating a column via select called is_color_holder with SELECT only_json.value ? 'color' as is_color_holder. This column populates correctly, but if I use a where clause, WHERE is_color_holder = true, I receive the same error.
Repeating the value_type='json' expression in the problematic where clause.
Moving the cast up a subquery.
Replacing the join with where id in (select id from public.test_types where value_type = 'json')
Comma-style joins.
Centering the query around the types table first, then joining the value type after the types have already been filtered.
Is this a bug I should report to postgres? Am I missing something?
Edit: I managed one workaround. See my answer for more details. Still looking for a better answer, though.
select id, value from (
select id, case when value_type = 'json' then value::jsonb else to_jsonb(value) end as value, value_type from
public.test_table natural join public.test_types
where value_type = 'json') as_json
where value ? 'color' = true
id|value |
--|-----------------|
3|{"color": "blue"}|
I suspect that what you are seeing is premature optimization, caused by predicate pushdown.
In Postgres, a common strategy to avoid that is the offset 0 hack:
select id, value from (
select id, value
from public.test_table
inner join public.test_types using(id)
where value_type = 'json'
offset 0 -- (try to) prevent predicate pushdown
) only_json
where value::jsonb ? 'color'
Demo on DB Fiddle
I found a workaround. I'll post as an 'answer' here and edit the above question.
But if anyone has a better answer for me, I'll make yours as the correct one.
Casting non-json values to json with "CASE" works fine:
select id, value from (
select id, case when value_type = 'json' then value::jsonb else to_jsonb(value) end as value, value_type from
public.test_table natural join public.test_types
where value_type = 'json') as_json
where value ? 'color' = true
id|value |
--|-----------------|
3|{"color": "blue"}|

MYSQL insert from select and variables

I am trying to insert values coming from a select and variable :
INSERT INTO routeur (`codeAdherent`, `quantiteArticle`, `dateFin`) VALUES
(SELECT `codeAdherent` FROM adherents WHERE categorie = 'G', `quantiteArticle` = $a, `dateFin`= $b);
Write it with and without VALUES, with and without IN, with and without brackets but I always get an synthax error.
Where is my mistake?
Try below:
INSERT INTO routeur (codeAdherent, quantiteArticle, dateFin)
SELECT codeAdherent, #a, #b FROM adherents WHERE categorie = 'G'
You have to read carefully the INSERT syntax because you have got many errors.
This is the right syntax:
INSERT INTO routeur (codeAdherent, quantiteArticle, dateFin)
SELECT codeAdherent, '$a', '$b'
FROM adherents
WHERE categorie = 'G'
PS: To avoid the SQL Injection you should use Prepared Statements
You can try this out :
INSERT INTO routeur (codeAdherent, quantiteArticle, dateFin) VALUES
(SELECT codeAdherent FROM adherents WHERE categorie = 'G', $a, $b);

MySQL Convert Numbers to letters

I have an auto-increment transactionID type=MEDIUMINT(9) in my table. I want to also display a unique 4-character (which can grow over time, but 4 for now) alphabetical Redemption Code to my users. What is the best way to derive this alphabetical code from my transactionID, preferably straight from the SELECT statement?
That mostly depends on what alphabet you want to use.
You may use TO_BASE64 to convert it it to base64 encoded string or simply do something like:
select REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
REPLACE(your_number, '0', 'A')
, '1', 'B')
, '2', 'C')
, '3', 'D')
, '4', 'E')
, '5', 'F')
, '6', 'G')
, '7', 'H')
, '8', 'I')
, '9', 'J')
if you want custom alphabet.
In case you want something shorter, you can go a slightly harder way:
You use 9-digit decimal (maximum 999999999), which translates to 8 hex digits (0x3B9AC9FF), i.e. 4 bytes. What you can do is divide your number in 4 binary octets, convert them to chars, construct new string and feed it to TO_BASE64():
select TO_BASE64(CONCAT(CHAR(FLOOR(your_number/(256*256*256))%256),CHAR(FLOOR(your_number/(256*256))%256),CHAR(FLOOR(your_number/256)%256),CHAR(your_number%256)))
Note, that TO_BASE64() function is available only in MySQL 5.6 on-wards.
Now, for those on older versions - we don't want to implement base64 encoding with our bare hands, don't we? So, lets go the easier way: we have 30 bits in those 9 decimal digits, which would be 30/6=5 characters, if we use 64 continuous character alphabet after CHAR(32), which is space, which we don't want to use:
SELECT CONCAT(`enter code here`CHAR(FLOOR(your_number/(64*64*64*64))%64+33),CHAR(FLOOR(your_number/(64*64*64))%64+33),CHAR(FLOOR(your_number/(64*64))%64+33),CHAR(FLOOR(your_number/64)%64+64),CHAR(your_number%64+33))
I was just looking for something like this and I found a way to do it with the CONV function.
CONV(9+your_number, 10, 36)
This converts 1 to A, 2 to B etc.
The way it works is by adding 9 and then converting to base 36, in which 10 is A, 11 is B etc.
You can generate a hash with the value of transaction id.
Like:
SELECT MD5(transactionID) FROM `yourtable`;
There are several other types of similar functions you can use.
Maybe can use CASE WHEN() END; and stored procedure
For example:
CREATE DEFINER = 'USERNAME'#'HOST' STORED PROCEDURE `ConvertNumberToAlphabetic`
BEGIN
SELECT
(CASE
WHEN (your_number = '1') THEN 'A'
WHEN (your_number = '2') THEN 'B'
WHEN (your_number = '3') THEN 'C'
WHEN (your_number = '4') THEN 'D'
WHEN (your_number = '5') THEN 'E'
WHEN (your_number = '6') THEN 'F'
WHEN (your_number = '7') THEN 'G'
WHEN (your_number = '8') THEN 'H'
WHEN (your_number = '9') THEN 'J'
WHEN (your_number = '10') THEN 'K'
END) RedeemCode
FROM tblTransaction;
END
SELECT SUBSTRING(MD5(transactionId) FROM 1 FOR 4) AS RedemptionCode
This creates a 4 character (easy to change, as you can see) string where the characters are from the MD5 command (and therefore in the range a-z and 0-9).

Increase Alphanumeric VARCHAR Entry by Value 1?

On an old project because of not thought through design I have a column which actually should be set to auto_increment, though it cannot be because it are alphanumeric entries as follows:
c01
c02
c03
(c99 would continue to c100 and more), the letter happened in the past and it would require to overhaul the system to take it out, thus I rather prefer this workaround.
Now I need a way to imitate the auto_increment functionality with the SQL statement myself, my own attempt has gotten as far as the following:
INSERT INTO tags (tag_id, tag_name, tag_description, added_by_user_id, creation_date, last_edited) VALUES (SELECT(MAX(tag_id)+1),
'Love', 'All about love', 7, now(), 0);
This one does not work as is, though the idea was to select the highest entry in the column "tag_id" and then simply increase it by the value 1.
Any ideas how to accomplish this?
By the way I am also not sure if you simply can increase an alphanumeric entry through this way, though I know it can be done, I just don't know how.
If you want to safely get the largest integer value of a tag id of the form c##.., you could use the following expression:
max( convert( substring(tag_id, 2) , unsigned integer) )
^^^ largest ^^^^^^^^^ after 'c' ^^^^^^^^^^^^^^^^ convert to positive number
Then your insert statement would look something like this:
set #newid = convert(
(select
max(convert( (substring(tag_id, 2)) , unsigned integer))+1
from tags), char(10)
);
set #newid = if(length(#newid) = 1, concat('0', #newid), #newid);
set #newid = concat('c', #newid);
INSERT INTO tags (tag_id, tag_name, tag_description, added_by_user_id,
creation_date, last_edited)
VALUES (#newid, 'Love', 'All about love', 7, now(), '2012-04-15');
Demo: http://www.sqlfiddle.com/#!2/0bd9f/1
this will increase from c01 to c02 to c03 ... to c99 to c100 to c101 ... to c999 to c1000 etc.
set #nextID = (SELECT CONCAT(SUBSTRING(`tag_id`, 1, 1), IF(CHAR_LENGTH(CAST(SUBSTRING(`tag_id`, 2)
AS UNSIGNED)) < 2, LPAD(CAST(CAST(SUBSTRING(`tag_id`, 2) AS UNSIGNED) + 1 AS CHAR), 2,
'0'), CAST(CAST(SUBSTRING(`tag_id`, 2) AS UNSIGNED) + 1 AS CHAR))) FROM `tags` ORDER BY
`tag_id` DESC LIMIT 1);
INSERT INTO tags (tag_id, tag_name, tag_description, added_by_user_id,
creation_date, last_edited) VALUES (#nextID, 'Love', 'All about love', 7, NOW(), null);

MySQL SELECT only if doesn't match

I'm trying to do a select from a table based on the value of a name field.
I want to only match certain criteria if a record is not based on the first, ie.
if the following matches:
WHERE name='version'
if so return that single row, if not, look for these too:
WHERE name='v' OR name='e' OR name='r'
etc...
Is this possible in a single query?
Many thanks!
SELECT ...
FROM ..
WHERE name = 'version'
UNION ALL
SELECT ...
FROM ...
WHERE NOT EXISTS (
SELECT ...
FROM ..
WHERE name = 'version'
) AND name IN ('v', 'e', 'r', ...)
EDIT:
Not sure how to test if mysql would cache the results of the first query, but assuming you run the query in a new session:
SELECT #cached:=1, ...
FROM ..
WHERE name = 'version'
UNION ALL
SELECT 2, ...
FROM ...
WHERE #cached IS NULL AND name IN ('v', 'e', 'r', ...)
should work
If in the same session you want to clear the #cached with
SELECT #cached:=1, ...
FROM .., (SELECT #cached:=0) x
WHERE name = 'version'
UNION ALL
SELECT 2, ...
FROM ...
WHERE #cached = 0 AND name IN ('v', 'e', 'r', ...)
select ...
from ..
where (name = 'version') or (name in ('v', 'e', 'r', ...))
Though, since you're basing the comparison on the same field, there's no difference between the above version and
...
where (name in ('version', 'v', 'e', 'r', ...))