MySQL Variable Substitution - mysql

I want to simplify the following using dynamic SQL like one could do in Transact SQL.
I want to do something like:
SET #s = replace(field_name, '_complete','')
and use #s instead of replace(field_name, '_complete','')
Please adive if possible and if so how.
My current code:
select distinct
if(instr(replace(field_name, '_complete',''),'_') <= 5
,left(replace(field_name, '_complete','')
,instr(replace(field_name, '_complete',''),'_') - 1
)
,replace(field_name, '_complete','')
) AS form_id ,replace(
if(instr(replace(field_name, '_complete',''),'_') <= 5,
mid(replace(field_name, '_complete',''),
instr(replace(field_name, '_complete',''),'_') + 1,
length(replace(field_name, '_complete','')) - instr(replace(field_name, '_complete',''),'_')
)
,replace(field_name, '_complete','')
),
'_',
' ') as form_name ,field_name from redcap_extract2use where field_name like '%_complete' order by 1;
The above would then be replaced with:
select distinct
if(instr(#s,'_') <= 5 ,left(#s,instr(#s,'_') - 1),#s ) AS form_id
,replace( if(instr(#s,'_') <= 5,
mid(#s,instr(#s,'_') + 1,length(#s) - instr(#s,'_')),#s), '_', ' ') as form_name
,field_name
from redcap_extract2use
where field_name like '%_complete'
order by 1;
and I would have an execute... to run the query

If I'm understanding your question correctly then you would want to use the PREPARE and the EXECUTE statements.
For example:
SET #s = replace(field_name, '_complete','');
PREPARE mystatement FROM
SELECT DISTINCT ...... ;
EXECUTE mystatement;

Related

MySQL query error "Subquery returns more than 1 row" where update into the same table

How to update to the same table? i tried to do fraction conversion and update 1 of the rows in the same table but the error "Subquery returns more than 1 row"
Below are the code, (State1 is the table itself, Share field consists of those fraction data like 1/2,1/5 ..)
UPDATE State1 set shareresult =(SELECT
x.Multiplier / x.Divider as Result
from
(select
cast( substr( t.share,
1,
locate('/', t.share) - 1)
as decimal)
as Multiplier,
cast( substr( t.Share,
locate('/', t.Share) + 1,
locate( ' ',
concat(t.Share, ' ')))
as decimal)
as Divider
from
State1 t ) x)
thanks
Finally, i managed to solve my problem with the code below
CREATE TEMPORARY TABLE activity_product_ids AS SELECT x.NumberID, x.Multiplier / x.Divider as Result from (select cast( substr( t.share, 1, locate('/', t.share) - 1) as decimal) as Multiplier, cast( substr( t.Share, locate('/', t.Share) + 1, locate( ' ', concat(t.Share, ' '))) as decimal) as Divider, t.ID as numberID from State1 t) x;
UPDATE State1 a
JOIN activity_product_ids b
ON a.ID=b.NumberID
SET a.shareresult=b.Result;
Thanks for all the replies.
Neither your update statement nor the sub-query has a WHERE clause so it looks like you are trying to update every row with fraction to decimal conversion of every row.
This is a much simpler approach -
UPDATE State1
SET shareresult = ROUND(SUBSTRING_INDEX(share, '/', 1) / SUBSTRING_INDEX(share, '/', -1), 2)
WHERE id = 1;

Auto increment temporary column in select statement MySQL 8

Hello i use this type of sentence to return an auto increment column in my prepared statement selects
cnt := cnt + 1
SET #query = CONCAT('SELECT * FROM (SELECT (#cnt := #cnt + 1) AS id, a.idProducto idProducto, a.idExProducto idExterno, trim(a.descripcion) nombreProducto, trim(a.descripcionAlt) nombreLargoProducto, trim(a.serial) serial,',
' a.peso, a.volumen, a.IdTpoGrupo, trim(b.descripcion) nombreGrupo,',
' a.idTpoLinea, trim(c.descripcion) nombreLinea,',
' a.idTpoMarca, trim(d.descripcion) nombreMarca,',
' a.idTpoUnidad, trim(e.descripcion) nombreTipoUnidad,',
' CASE WHEN a._estado = 1 THEN ''true'' ELSE ''false'' END estado, ',
' g.nombreArchivo imagen ',
' FROM tblProducto a',
' INNER JOIN tblProducto_TpoGrupo b ON a.IdTpoGrupo = b.IdTpoGrupo',
' INNER JOIN tblProducto_TpoLinea c ON a.idTpoLinea = c.idTpoLinea',
' INNER JOIN tblProducto_TpoMarca d ON a.idTpoMarca = d.idTpoMarca',
' INNER JOIN tblTpoUnidadMedida e ON a.idTpoUnidad = e.idTpoUnidadMed ',
' LEFT JOIN tblProductoXImagen f ON a.idProducto = f.idProducto AND f._estado=1',
' LEFT JOIN tblImagen g on f.idImagen = g.idImagen AND g._estado=1',
' WHERE a._estado<2 ',whereLike,whereConcat, ' order by idProducto ) allrecords');
PREPARE stmt FROM #query;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
that works perfect on MySQL 5, but now in MySQL 8 is deprecated and my transaction return a warning, but the warning stop the execution of my stored procedure, i want to know if there is a solution to can achieve this behavior in MySQL 8.
Thanks for your help.
You can use window functions. The logic you sem to want is:
row_number() over(order by idProducto) as id
This gives you an incrementing integer value, that starts at 1 and increments according to idProducto. If two rows in the resultset have the same idProducto, it is undefined which one will be ordered "first" (it is, however, guaranteed that they will not get the same row number) - in that case, you might want to add one or more columns to the order by clause, so you do get a predictable, stable result.

How to optimize this MySQL query from Doctrine QueryBuilder?

This query is generated by a doctrine2 QueryBuilder (the concat function takes only 2 parameters), and it takes 4 seconds.
SELECT COUNT(*) AS dctrn_count
FROM
(
SELECT DISTINCT id_4
FROM
(
SELECT 1 / LOCATE( ?, CONCAT( CONCAT( CONCAT(w0_.firstname, ' '),
CONCAT(w0_.lastname, ' ') ), w1_.fullname )
) AS sclr_0,
1 / LOCATE( ?, CONCAT( CONCAT( CONCAT(w0_.firstname, ' '),
CONCAT(w0_.lastname, ' ') ), w1_.shortname )
) AS sclr_1,
1 / LOCATE( ?, CONCAT( CONCAT( CONCAT(w0_.nickname, ' '),
CONCAT(w0_.lastname, ' ') ), w1_.fullname )
) AS sclr_2,
1 / LOCATE( ?, CONCAT( CONCAT( CONCAT(w0_.nickname, ' '),
CONCAT(w0_.lastname, ' ') ), w1_.shortname )
) AS sclr_3,
w0_.id AS id_4, w0_.slug AS slug_5, w0_.firstname AS firstname_6,
w0_.lastname AS lastname_7, w0_.nickname AS nickname_8,
w0_.gender AS gender_9, w0_.email AS email_10, w0_.email_checked AS email_checked_11,
w0_.title_en AS title_en_12, w0_.short_title AS short_title_13,
-- lots of stuff removed (see edit) --
w5_.biography_en AS biography_en_55, w5_.created AS created_56, w5_.updated AS updated_57, w6_.id AS id_58, w6_.web_text AS web_text_59, w6_.created AS created_60
FROM wmn_executive w0_
INNER JOIN wmn_company w1_ ON w0_.company_id = w1_.id
INNER JOIN wmn_industry w7_ ON w1_.industry_id = w7_.id
INNER JOIN wmn_location w2_ ON w1_.location_id = w2_.id
INNER JOIN wmn_country w3_ ON w2_.country_id = w3_.id
INNER JOIN wmn_city w4_ ON w2_.city_id = w4_.id
LEFT JOIN wmn_executive_link w5_ ON w0_.link_id = w5_.id
LEFT JOIN wmn_web_executive w6_ ON w0_.id = w6_.executive_id
WHERE w0_.original_id IS NULL
AND w0_.user_id IS NOT NULL
AND ( w0_.firstname LIKE ?
OR w0_.lastname LIKE ?
OR w0_.nickname LIKE ?
OR w1_.fullname LIKE ?
OR w1_.shortname LIKE ?
OR w0_.title_en LIKE ?
OR w0_.short_title LIKE ?
OR w7_.industry_name_en LIKE ?
OR w7_.industry_name_fr LIKE ?
OR w3_.country_name_en LIKE ?
OR w3_.country_name_fr LIKE ?
OR w4_.city_name LIKE ?
)
ORDER BY sclr_0 DESC, sclr_1 DESC, sclr_2 DESC, sclr_3 DESC ) dctrn_result
) dctrn_table
** The ORDER BY provides no benefit to the end result; remove it.
**
SELECT COUNT(*) AS dctrn_count
FROM
(
SELECT DISTINCT id_4
can be simplified to
SELECT COUNT(DISTINCT(id_4))
** All the items in the SELECT clause are not use, except for id_4; get rid of them.
**** Those 3 optimization might shrink the run time from 4.0s to maybe 3.9s.
And then you will say that this is not the real query, but merely a count?
If you are going to do a messy text scan like that, you need all those strings in one table. Better yet, all the strings concatenated together into one column in one table. This would be just for searching, not for display. Then make a FULLTEXT index on that column. This will solve the OR and LIKE '%...' problems. But how to get it back into doctrine2, I don't know.

Mysql replace not all string?

I have column(pro_doc) string like [1,2,11,]
UPDATE product SET prod_doc = REPLACE(prod_doc, '1,' , '') WHERE prod_id = 2
The result is 2,1
I want become 2,11,
I just want replace first 1, not all 1,
Have any solution?
Thanks a lot...
Use SUBSTRING_INDEX.
Find the total occurence of , and use negative of that as the last argument of the SUBSTRING_INDEX function.
UPDATE product
SET prod_doc =
SUBSTRING_INDEX(
prod_doc, ',', -(
ROUND(
(
LENGTH(prod_doc) - LENGTH(REPLACE(prod_doc, ',', ''))
) / LENGTH(','))
)
)
WHERE prod_id = 2;
SQL Fiddle Demo

MySQL: Query for list of available options for SET

In particular table there exists a field of SET type with specific legal values:
personType SET('CUSTOMER','SUPPLIER','EMPLOYEE', 'CONTRACTOR') NOT NULL
Is there any way to query MySQL to get a list of the valid values? In the MySQL interpreter I would just run DESCRIBE someTable; however if there is a more direct method that one could use programmatically without lots of parsing it would be nice.
Thanks.
Now, this simply freaks out, but it is MySQL-only and it works!
SELECT TRIM("'" FROM SUBSTRING_INDEX(SUBSTRING_INDEX(
(SELECT TRIM(')' FROM SUBSTR(column_type, 5)) FROM information_schema.columns
WHERE table_name = 'some_table' AND column_name = 'some_column'),
',', #r:=#r+1), ',', -1)) AS item
FROM (SELECT #r:=0) deriv1,
(SELECT ID FROM information_schema.COLLATIONS) deriv2
HAVING #r <=
(SELECT LENGTH(column_type) - LENGTH(REPLACE(column_type, ',', ''))
FROM information_schema.columns
WHERE table_name = 'some_table' AND column_name = 'some_column');
Just replace "some_table" and "some_column" for your specific table/column, and see the magic!
You will see a weird usage of "information_schema.COLLATIONS" - this is because we need a table there - any table - containing at least N rows, where N is the number of elements in your set.
SELECT
column_type
FROM
information_schema.columns
WHERE
table_name = 'some_table'
AND
column_name = 'some_column';
Returns:
column_type
------------------
set('this','that')
The function below returns an array containing all available options for SET with some parsing but not "lots of parsing"... :)
function get_set_values($table_name, $field_name)
{
$sql = 'DESCRIBE ' . $table_name . ' ' . $field_name;
$result = mysql_query($sql);
$row = mysql_fetch_array($result);
return str_getcsv( trim( substr( $row['Type'], 3 ), '()' ), ',', "'" );
}
Remember that in a set column you may have a combination of values or even an empty value (these are also valid).