MySQL OutFile to use data from table - mysql

I'm trying to generate an export that runs monthly which appends a value from one of the columns within a table to the OutFile name, is this even possible?
The table is mytable and the column is col1 which contains "201601" at the minute
I would like my OutFile to show "/tmp/Output_201601" where the 201601 on next run will have automatically changed to 201602 etc

You can try to do this with stored procedure.
Eg. when you call this procedure - it export data from table mytable to file with name D:/tmp/Output_ + top value from field Col1.
CREATE PROCEDURE `PrepareReport`()
BEGIN
SET #OutputFileName := 'D://tmp//Output_';
SELECT Col1 INTO #ReportNr FROM mytable ORDER BY Col1 DESC LIMIT 1;
SET #q1 := concat("SELECT * FROM mytable INTO OUTFILE '", #OutputFileName, #ReportNr, ".csv'");
PREPARE s1 FROM #q1;
EXECUTE s1;
DEALLOCATE PREPARE s1;
END

Related

How do I copy a table if a specific condition is met?

I can determine whether a condition is met by using the following:
SELECT id FROM admin WHERE firstname='John' INTO #tmp
SELECT IF (#tmp >= 1, "success", "fail")
But how would I create a copy of the table if the same condition is met?
eg.
CREATE TABLE admin_copy LIKE admin
INSERT admin_copy
SELECT * FROM admin;
This might work for you.
SET #create_table := IF(#tmp >= 1, 'CREATE TABLE admin_copy LIKE admin', 'SELECT 0')
;
PREPARE stmt_create FROM #create_table
;
EXECUTE stmt_create
;
DEALLOCATE PREPARE stmt_create
;

select * from t limit #var

I am using multi-value option on grafana to select metrics and display them dynamically, this method works on graphs fine because I am not putting any limiter but I want to use Gauge to display the last entered data and the selected options to show the desired metric like this
.
And here i am stuck.
here is things i tried but naah didn't work,;
I tried to create a procedure for test;
CREATE PROCEDURE getData()
BEGIN
SET #itemlimit := JSON_LENGTH(JSON_ARRAY('conductivite0','conductivite1'));
SELECT UNIX_TIMESTAMP(datetime) as time_sec,
value as value,
label as metric
FROM valeur_capteur_SD
WHERE label in ('conductivite0','conductivite1')
ORDER BY id DESC LIMIT 0, #itemlimit;
END //
DELIMITER ;
and using a variable,
SET #var =
(
SELECT vars
FROM (
SELECT
JSON_LENGTH(
REPLACE(
CONCAT("[", "'conductivite0','conductivite1'", "]"),"'",'"'
)
) 'vars') as vars
);
SELECT
UNIX_TIMESTAMP(datetime) as time_sec,
value as value,
label as metric
FROM valeur_capteur_SD
WHERE label in ('conductivite0','conductivite1','debit0')
ORDER BY id DESC
LIMIT #var;
Well, after struggling for hours i found out a way to deal with it
DELIMITER //
CREATE PROCEDURE `getSelectedSensors`(IN param TEXT)
BEGIN
SET #query1 := CONCAT("
SELECT UNIX_TIMESTAMP(datetime) as time_sec,value as value, label as metric
FROM valeur_capteur_SD WHERE label in (",param,")
ORDER BY id DESC
LIMIT 0, ?");
PREPARE stmt1 FROM #query1;
SET #itemlimit = (LENGTH(param) - LENGTH(REPLACE(param, ',', ''))) +1;
EXECUTE stmt1 USING #itemlimit;
DEALLOCATE PREPARE stmt1;
END;

How to set a local list/tuple variable in mysql

Is there a way to do the following in mysql?
SET #studios = ('Disney', 'Warner Bros.', 'Fox');
SELECT * FROM movies WHERE provider IN #studios;
When I try doing the above I get the error:
Operand should contain 1 column(s)
The error is coming from your initial assignment. You cannot assign lists to variables.
The only way of doing this in MySQL is to either create a temp table to hold the values, and then do ... IN (SELECT someVal FROM thatTemp), or to dynamically create the query with the values directly in the query string.
Example temp table creation:
CREATE TEMPORARY TABLE `someTemp` ( someVal VARCHAR(16) );
INSERT INTO `someTemp` (someVal) VALUES ('a'), ('b'), ('c');
SELECT * FROM myTable WHERE myField IN (SELECT someVal FROM someTemp);
DELETE TEMPORARY TABLE `someTemp`;
Alternatively, there is also FIND_IN_SET, which could be used like this:
SET #list = 'a,b,c';
SELECT * FROM myTable WHERE FIND_IN_SET(myField, #list) <> 0;
but this method probably has extremely poor performance (and may not be useable if your "myField" values may contain commas).
It is not possible to set a tuple/list/array in a user-defined variable in MySQL. You can use Dynamic SQL for the same:
-- we use single quotes two times to escape it
SET #studios = '(''Disney'', ''Warner Bros.'', ''Fox'')';
-- generate the query string
SET #query = CONCAT('SELECT * FROM movies WHERE provider IN ', #studios);
-- prepare the query
PREPARE stmt FROM #query;
-- execute it
EXECUTE stmt;
-- deallocate it
DEALLOCATE PREPARE stmt;
You could concatenate your list to a string, and use FIND_IN_SET as your criteria. Might not be super efficient, but makes the code quite easy to read and maintain.
Looks like this:
SET #studios = CONCAT_WS(',',
'Disney',
'Warner Bros.',
'Fox'
);
SELECT * FROM movies
WHERE FIND_IN_SET(provider, #studios) <> 0;

Table name as field

Is it possible to query a table whose name comes from a sub-query?
For example.,
SELECT * FROM <TABLE_NAME IS <SUB_QUERY>>
select * from (
(select distinct(name) from category where id = 3 limit 1) CAT);
INNER QUERY RESULTS --> DEPARTMENT;
So it has to fetch from department table.
Using Mysql as DB.
You should use Prepared Statements.
In your case it should be:
select #name := name from (
(select distinct(name) from category where id = 3 limit 1) CAT);
set #sqlquery := 'select * from ' . #name ;
prepare qry from #sqlquery ;
execute qry;
deallocate prepare qry;
This might be helpful SQL Syntax for Prepared Statements
In two words: you can execute sql commands specified in varchar variables which can be produced by concatenation and other stuff.

How can I assign a variable with a prepared statement in a stored procedure?

I've put together a simple stored procedure in which two parameters are passed through to make it more dynamic. I've done this with a prepared statement in the "First Two Digits and Count of Records" section.
What I'm not sure of is if I can make the SET vTotalFT section dynamic with a prepared statement as well.
At the moment I have to hard-code the table names and fields. I want my vTotalFT variable to be assigned based on a prepared dynamic SQL statement, but I'm not sure of the syntax. The idea is that when I call my procedure, I could tell it which table and which field to use for the analysis.
CREATE PROCEDURE `sp_benfords_ft_digits_analysis`(vTable varchar(255), vField varchar(255))
SQL SECURITY INVOKER
BEGIN
-- Variables
DECLARE vTotalFT int(11);
-- Removes existing table
DROP TABLE IF EXISTS analysis_benfords_ft_digits;
-- Builds base analysis table
CREATE TABLE analysis_benfords_ft_digits
(
ID int(11) NOT NULL AUTO_INCREMENT,
FT_Digits int(11),
Count_of_Records int(11),
Actual decimal(18,3),
Benfords decimal(18,3),
Difference Decimal(18,3),
AbsDiff decimal(18,3),
Zstat decimal(18,3),
PRIMARY KEY (ID),
KEY id_id (ID)
);
-- First Two Digits and Count of Records
SET #s = concat('INSERT INTO analysis_benfords_ft_digits
(FT_Digits,Count_of_Records)
select substring(cast(',vField,' as char(50)),1,2) as FT_Digits, count(*) as Count_of_Records
from ',vTable,'
where ',vField,' >= 10
group by 1');
prepare stmt from #s;
execute stmt;
deallocate prepare stmt;
SET vTotalFT = (select sum(Count_of_Records) from
(select substring(cast(Gross_Amount as char(50)),1,2) as FT_Digits, count(*) as Count_of_Records
from supplier_invoice_headers
where Gross_Amount >= 10
group by 1) a);
-- Actual
UPDATE analysis_benfords_ft_digits
SET Actual = Count_of_Records / vTotalFT;
-- Benfords
UPDATE analysis_benfords_ft_digits
SET Benfords = Log(1 + (1 / FT_Digits)) / Log(10);
-- Difference
UPDATE analysis_benfords_ft_digits
SET Difference = Actual - Benfords;
-- AbsDiff
UPDATE analysis_benfords_ft_digits
SET AbsDiff = abs(Difference);
-- ZStat
UPDATE analysis_benfords_ft_digits
SET ZStat = cast((ABS(Actual-Benfords)-IF((1/(2*vTotalFT))<ABS(Actual-Benfords),(1/(2*vTotalFT)),0))/(SQRT(Benfords*(1-Benfords)/vTotalFT)) as decimal(18,3));
First, to use dynamic table/column names, you'll need to use a string/Prepared Statement like your first query for #s. Next, to get the return-value from COUNT() inside of the query you'll need to use SELECT .. INTO #vTotalFT.
The following should be all you need:
SET #vTotalFTquery = CONCAT('(select sum(Count_of_Records) INTO #vTotalFT from
(select substring(cast(', vField, ' as char(50)),1,2) as FT_Digits, count(*) as Count_of_Records
from ', vTable, '
where ', vField, ' >= 10
group by 1) a);');
PREPARE stmt FROM #vTotalFTquery;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Please note: the variable name has changed from vTotalFT to #vTotalFT. It doesn't seem to work without the #. And also, the variable #vTotalFT won't work when declared outside of/before the query, so if you encounter an error or empty results that could be a cause.
SELECT CONCAT (
'SELECT DATE(PunchDateTime) as day , '
,GROUP_CONCAT('GROUP_CONCAT(IF(PunchEvent=', QUOTE(PunchEvent), ',PunchDateTime,NULL))
AS `', REPLACE(PunchEvent, '`', '``'), '`')
,'
FROM tbl_punch
GROUP BY DATE(PunchDateTime)
ORDER BY PunchDateTime ASC
'
)
INTO #sql
FROM (
SELECT DISTINCT PunchEvent
FROM tbl_punch
) t;
PREPARE stmt
FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;