Passing columns dynamically in mysql query - mysql

My requirement is to fetch value from mysql table by passing column names dynamically which I get from function. But the problem is, it displays it as it is rather then fetching the value of column.
The select query is:
SET #stqry =
CONCAT('SELECT DISTINCT
FD.FormsDataID AS FormDataID,
FC.ColumnName AS FieldName,
IFNULL(FC.CustomDisplayName,FC.ColumnName) AS DisplayName,
GetColumnValue(',_FormID,',FC.ColumnName) AS FieldValue,
GetColumnValue(FC.FormID,FC.ColumnName) AS ColIndex
FROM FormsData FD
LEFT JOIN FormsColumns FC ON FD.FormID = FD.FormID
LEFT JOIN FormsRelationship FR ON FR.FormID = FC.FormID
WHERE FD.FormID = ',_FormID,' AND FD.ID = ',_recordID
,' AND FC.FormID=',_FormID
,' AND FD.PARENT_RECORD_ID = 0 AND FC.IsDisplayInEmail = 1
AND FC.IsActive = 1');
select #stqry;
PREPARE n_StrSQL FROM #stqry;
EXECUTE n_StrSQL;
GetColumnValue()is the function I've made which returns columnname from table.(I need it because columns names are added by user dynamically).
The result I am getting is:
FormDataID FieldName fieldValue
-------------------------------
2497 date_time [1]
2497 auto_email [2]
2497 more_email [3]
But actual result should fetch values of [1],[2],[3] in third column. What am I missing here?Can any one help?

Related

How can you reference a column based on the average on another column in MySQL?

We have a scenario where users answer some questions related to a parent entity that we'll call a widget. Each question has both a numeric and word answer. Multiple users answer each question for a given widget.
We then display a row for each widget with the average numeric answer for each question. We do that using a MySQL pseudo-pivot with dynamic columns as detailed here So we end up with something like:
SELECT widget_id, ...
ROUND(IFNULL(AVG(CASE
WHEN LOWER(REPLACE(RQ.question, ' ', '_')) = 'overall_size' THEN
if(RA.num = '', 0, RA.num) END),0) + .0001, 2) AS `raw_avg_overall_size`,
...
... where overall_size would be one of the question types related to the widget and might have "answers" from 5 users like 1,2,2,3,1 to that question for a given widget_id based on the answer options below:
Answers
answer_id
answer_type
num
word
111
overall_size
1
x-large
112
overall_size
2
large
113
overall_size
3
medium
114
overall_size
4
small
115
overall_size
5
x-small
So we would end up with a row that had something like this:
widget_id
average_overall_size
115
1.80
What we can't figure out is then given if we round 1.80 to zero precision we get 2 in this example which is the word value 'large' from our data above. We like to include that in the query output too so that end up with:
widget_id
raw_average_overall_size
average_overall_size
115
1.80
large
The issue is that we do not know the average for the row until the query runs. So how can we then reference the word value for that average answer in the same row when executing the query?
As mentioned we are pivoting into a variable and then run another query for the full execution. So if we join in the pivot section, that subquery looks something like this:
SET #phase_id = 1;
SET SESSION group_concat_max_len = 100000;
SET #SQL = NULL;
SET #NSQL = NULL;
SELECT GROUP_CONCAT(DISTINCT
CONCAT(
'ROUND(IFNULL(AVG(CASE
WHEN LOWER(REPLACE(RQ.short_question, '' '', ''_'')) = ''',
nsq,
''' THEN
if(RA.answer = '''', 0, RA.answer) END),0) + .0001, 2) AS `',
CONCAT('avg_raw_',nsq), '`,
REF.value, -- <- ******* THIS FAILS **** --
ROUND(IFNULL(STDDEV(CASE
WHEN LOWER(REPLACE(RQ.short_question, '' '', ''_'')) = ''',
nsq,
''' THEN RA.answer END), 0) + .0001, 3) AS `',
CONCAT('std_dev_', nsq), '`
'
)
ORDER BY display_order
) INTO #NSQL
FROM (
SELECT FD.ref_value, FD.element_name, RQ.display_order, LOWER(REPLACE(RQ.short_question, ' ', '_')) as nsq
FROM review_questions RQ
LEFT JOIN form_data FD ON FD.id = RQ.form_data_id
LEFT JOIN ref_values RV on FD.ref_value = RV.type
WHERE RQ.phase_id = #phase_id
AND FD.element_type = 'select'
AND RQ.is_active > 0
GROUP BY FD.element_name
HAVING MAX(RV.key_name) REGEXP '^[0-9]+$'
) nq
/****** suggested in 1st answer ******/
LEFT JOIN ref_values REF ON REF.`type` = nq.ref_value
AND REF.key_name = ROUND(CONCAT('avg_raw_',nsq), 0);
So we need the word answer (from the REF join's REF.value field in the above code) in the pivot output, but it fails with 'Unknown column REF.value. If we put REF.value in it's parent query field list, that also fails with the same error.
You'll need to join the table/view/query again to get the 'large' value.
For example:
select a.*, b.word
from (
-- your query here
) a
join my_table b on b.answer_id = a.answer_id
and b.num = round(a.num);
An index on my_table (answer_id, num) will speed up the extra search.
This fails, leading to the default of "2":
LOWER(REPLACE(RQ.question, ' ', '_')) = 'overall_size'
That is because the question seems to be "average_overall_size", not "overall_size".
String parsing and manipulation is the pits in SQL; suggest using the application to handle such.
Also, be aware that you may need a separate subquery to compute aggregate (eg AVG()), else it might not be computed over the set of values you think.
Query into temp table, then join
First query should produce table as follows:
CREATE temp table, temp_average_size
widget_id
average_overall_size
rounded_average_size
115
1.80
2
LEFT JOIN
select s.*, a.word
from temp_average_size s LEFT JOIN answers a
ON (s.rounded_average_size = a.num AND a.answer_type = 'overall_size)

MySQL Update based on a query in another table

I want to update a table with a query from another table.
I want to get mixed informations from a combination of two tables.
And then update one of the two tables with them.
Here is what I did :
UPDATE commande as C,
(
SELECT CONCAT (input_hauteur_sous_collecteur, ' x ', input_largeur_hors_tout, ' x ', input_epaisseur, ' - ', input_pas_ailettes)
AS 'ligne_sage'
FROM commande as C, faisceaux_ta as F
WHERE C.commande_type_faisceaux = 'TA'
AND C.commande_id_faisceaux = F.id
) AS src
SET
C.ligne_sage = src.ligne_sage
WHERE
C.commande_type_faisceaux = "TA"
/* And I got MySQL running the command and never ending without error notification... */
EDIT : Actually it finally works in more than 5 minutes, the problem is that I have the same values (first line of the SELECT result table) in each lines...
What shall I do to make it work ?
(the SELECT CONCAT subquery is properly working)
You got this because you didn't filter results between tables. You need to add and filter in the where clause. Something like (see last line). I don't have your tables defs :-(
UPDATE commande as C,
(
SELECT CONCAT (input_hauteur_sous_collecteur, ' x ', input_largeur_hors_tout, ' x ', input_epaisseur, ' - ', input_pas_ailettes)
AS 'ligne_sage'
FROM commande as C, faisceaux_ta as F
WHERE C.commande_type_faisceaux = 'TA'
AND C.commande_id_faisceaux = F.id
) AS src
SET
C.ligne_sage = src.ligne_sage
WHERE
C.commande_type_faisceaux = "TA"
AND c.commandId = src.commandId

How detect the two word of a string like “helpme”?

I have a dictionary table (words) and another table with concatenated 2 words like "helpme", "helloword" "loveme"...
I want to transform this table to "help me", "hello word", "love me"
I run this sequence :
SELECT
table_concatened.twowords,
t1.word as 'word1',
t2.word as 'word2'
FROM
table_concatened
JOIN dictionary_table AS t1 ON SUBSTRING(table_concatened.twowords,1,len(t1.word)) = t1.word
JOIN dictionary_table AS t2 ON SUBSTRING(table_concatened.twowords,len(t1.word)+1,len(table_concatened.twowords)) = t2.word;
It is working, but is took a very long time with my table.
How can I optimise my sql sequence?
---- exemple of table ---
dictionary_table
|hello|
|word |
|love |
|me |
exemple of table_concatened :
|helloword|
|loveyou |
Edit:
1) The use case is for autocorrection. For example on skype, on iPhone, on chrome, when I type "helloword", I have auto correction to "hello word".
2) The database here is not very important. Our issue is about algo logic and performance optimisation.
If you don't mind going dynamic (and if SQL Server)
-- Generate Some Sample Data
Declare #Dictionary_Table table (word varchar(50));Insert Into #Dictionary_Table values ('hello'),('word'),('love'),('me')
Declare #table_concatened table (ID int,twowords varchar(50));Insert Into #table_concatened values (1,'helloword'),(2,'loveyou')
-- Generate SQL and Execute
Declare #SQL varchar(max)=''
Select #SQL = #SQL+concat(',(',ID,',''||',replace(twowords,'''',''''''),'||'')') From #table_concatened --Where ID=2
Select #SQL = Replace(#SQL,MapFrom,MapTo)
From (
Select MapFrom = word
,MapTo = '|'+ltrim(rtrim(word))+'|'
From #Dictionary_Table
Union All
Select '|',' ' -- Remove Any Remaining |
Union All
Select ' ',' ' -- Remove Any Remaining |
) A
Select #SQL = 'Select ID,Value=ltrim(rtrim(Value)) From ('+Stuff(#SQL,1,1,'values')+') N(ID,Value)'
Exec(#SQL)
Returns
ID Value
1 hello word
2 love you

MySQL user-defined function returns incorrect value when used in a SELECT statement

I met a problem when calling a user-defined function in MySQL. The computation is very simple but can't grasp where it went wrong and why it went wrong. Here's the thing.
So I created this function:
DELIMITER //
CREATE FUNCTION fn_computeLoanAmortization (_empId INT, _typeId INT)
RETURNS DECIMAL(17, 2)
BEGIN
SET #loanDeduction = 0.00;
SELECT TotalAmount, PeriodicDeduction, TotalInstallments, DeductionFlag
INTO #totalAmount, #periodicDeduction, #totalInstallments, #deductionFlag
FROM loans_table
WHERE TypeId = _typeId AND EmpId = _empId;
IF (#deductionFlag = 1) THEN
SET #remaining = #totalAmount - #totalInstallments;
IF(#remaining < #periodicDeduction) THEN
SET #loanDeduction = #remaining;
ELSE
SET #loanDeduction = #periodicDeduction;
END IF;
END IF;
RETURN #loanDeduction;
END;//
DELIMITER ;
If I call it like this, it works fine:
SELECT fn_computeLoanAmortization(3, 4)
But if I call it inside a SELECT statement, the result becomes erroneous:
SELECT Id, fn_computeLoanAmortization(Id, 4) AS Amort FROM emp_table
There's only one entry in the loans_table and the above statement should only result with one row having value in the Amort column but there are lots of random rows with the same Amort value as the one with the matching entry, which should not be the case.
Have anyone met this kind of weird dilemma? Or I might have done something wrong from my end. Kindly enlighten me.
Thank you very much.
EDIT:
By erroneous, I meant it like this:
loans_table has one record
EmpId = 1
TypeId = 2
PeriodicDeduction = 100
TotalAmount = 1000
TotalInstallments = 200
DeductionFlag = 1
emp_table has several rows
EmpId = 1
Name = Paolo
EmpId = 2
Name = Nikko
...
EmpId = 5
Name = Ariel
when I query the following statements, I get the correct value:
SELECT fn_computeLoanAmortization(1, 2)
SELECT Id, fn_computeLoanAmortization(Id, 2) AS Amort FROM emp_table WHERE EmpId = 1
But when I query this statement, I get incorrect values:
SELECT Id, fn_computeLoanAmortization(Id, 2) AS Amort FROM emp_table
Resultset would be:
EmpId | Amort
--------------------
1 | 100
2 | 100 (this should be 0, but the query returns 100)
3 | 100 (same error here)
...
5 | 100 (same error here up to the last record)
Inside your function, the variables you use to retrieve the values from the loans_table table are not local variables local to the function but session variables. When the select inside the function does not find any row, those variables still have the same values as from the previous execution of the function.
Use real local variables instead. In order to do that, use the variables names without # as a prefix and declare the variables at the beginning of the function. See this answer for more details.
I suspect the problem is that the variables in the INTO are not re-set when there is no matching row.
Just set them before the INTO:
BEGIN
SET #loanDeduction = 0.00;
SET #totalAmount = 0;
SET #periodicDeduction = 0;
SET #totalInstallments = 0;
SET #deductionFlag = 0;
SELECT TotalAmount, PeriodicDeduction, TotalInstallments, DeductionFlag
. . .
You might just want to set them to NULL.
Or, switch your logic to use local variables:
SET v_loanDeduction = 0.00;
SET v_totalAmount = 0;
SET v_periodicDeduction = 0;
SET v_totalInstallments = 0;
SET v_deductionFlag = 0;
And so on.

how to use dynamic query inside a function

i'm trying to create a function GETUSERPROP(prop, filter_prop, filter_value) that selects one item in database filtering by property and value
SET #_GET_PROP = "name";
SET #_FILTER_PROP = "id";
SET #_FILTER_VALUE = "15";
SET #q = CONCAT(
' SELECT ',
#_GET_PROP,
' FROM MYVIEW '
' WHERE 1 ',
' AND (`', #_FILTER_PROP, '` = "', #_FILTER_VALUE, '") '
);
PREPARE stmt FROM #q;
EXECUTE stmt;
the query outside function is running fine, but when I try to put this inside a function, I get the error
mysql dynamic sql is not allowed in stored function or trigger
I need this as a function to use inside other queries, like this:
SELECT
GETUSERPROP("name", "id", "15") AS name,
GETUSERPROP("age", "id", "15") AS age
From error message it is obvious we cannot use dynamic sql in function. Dynamic sql is only possible in procedure. So you have to think in alternative. If number of combination is not too much then you can use IF statement for each possible combination for example.
IF #_GET_PROP = 'name' AND #_FILTER_PROP = 'ID' THEN
SELECT name from MYVIEW where id = #_FILTER_VALUE;
ELSE IF #_GET_PROP = 'Agen' AND #_FILTER_PROP = 'ID' THEN
SELECT name from MYVIEW where id = #_FILTER_VALUE;
................
...............
and so on..
or something like this
SELECT CASE #_GET_PROP WHen 'name' then name when 'id' then ID when 'Age' then Age END from MYVIEW where
((#_FILTER_PROP = 'ID' AND id = #_FILTER_VALUE)
or (#_FILTER_PROP = 'name' AND name = #_FILTER_VALUE)
or (#_FILTER_PROP = 'age' AND age = #_FILTER_VALUE)
) ;