Auto-increment column names in pivot table - mysql

I'm creating a pivot table and I'd like the column names to be something like value1, value2, value3....
  CREATE TEMPORARY TABLE temp_table (
id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
value VARCHAR(255),
date DATETIME ,
person_id INT ) ;
Here's the part where I'm naming the column. I tried ID, but with multiple personIDs, it makes the id going in random. I was trying the INTEGER AUTO_INCREMENT, but that throws an error
SET SESSION group_concat_max_len = 1000000 ;
SELECT GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(id = ''', c.id, ''', value, NULL)) AS "', **INTEGER auto_increment**, '"'))
INTO #column_sql1
FROM temp_table c ;
set #sql = concat("select person_id, ", #column_sql1, " from temp_table group by `person_id`");
select #sql;
prepare stmt from #sql;
execute stmt;
Is there any way for each value column to be numbered, so later when I select I can choose the 1st, 2nd, and 3rd column instead of using a different field for the dynamic column name?
I've also tried row_number and many other things. Nothing seems to work.
Thanks for any help.
Error Message:
"You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'INTeger auto_increment, '"'))
INTO #column_sql1"

Related

Create table not exist error "Operand should contain 1 coulmns"

I checked some questions regarding this error and most of it experience this error on using "SELECT" function but in my case I am using CREATE TABLE IF NOT EXIST but experience this error, can you check my code to see what causes the error or if you guys know other way to do the same stuff.
I want to create a table for yearly payment actually that is supposed to be monthly but I am trying by year right now.
/* INSERT HERE THE LIST OF ALL EXISTING PAYMENT TABLES */
DROP TEMPORARY TABLE IF EXISTS T_distinctTable;
CREATE TEMPORARY TABLE T_distinctTable (
ctr int PRIMARY KEY AUTO_INCREMENT NOT NULL,
DisTable varchar (18)
);
INSERT INTO T_distinctTable (DisTable)
SELECT TABLE_NAME from information_schema.tables WHERE TABLE_SCHEMA = 'cams' and TABLE_NAME like concat ('%','Payment_20','%');
SET #endYearTemp = (select count(*) from T_distinctTable);
set #YearNow = (SELECT YEAR(CURDATE()));
set #TableName = concat('Payment_',#YearNow);
set #SQLTable = ('CREATE TABLE IF NOT EXISTS `', #TableName ,'` id int NOT NULL AUTO_INCREMENT, name CHAR(30),PRIMARY KEY (id)');
SELECT #SQLTable;
PREPARE stmt FROM #SQLTable;
EXECUTE stmt;
Use concat() to concatenate strings. Putting them in parenthesis and separate them with commas alone wouldn't do it. And you forgot the parenthesis around the column definitions of CREATE TABLE.
...
set #SQLTable = concat('CREATE TABLE IF NOT EXISTS `', #TableName ,'` (id int NOT NULL AUTO_INCREMENT, name CHAR(30),PRIMARY KEY (id))');
SELECT #SQLTable;
PREPARE stmt FROM #SQLTable;
EXECUTE stmt;
...

MySQL UDF/Stored function not recognizing table name passed as an argument

The following is the stored function:
DELIMITER //
CREATE FUNCTION `calcMedian`(
`tbl` VARCHAR(64),
`clm` VARCHAR(64)
) RETURNS decimal(14,4)
BEGIN
SELECT AVG(middle_values) AS 'median'
INTO medRslt
FROM (
SELECT t1.clm AS 'middle_values'
FROM
(
SELECT #row:=#row+1 as `row`, table_column_name
FROM tbl, (SELECT #row:=0) AS r
ORDER BY clm
) AS t1,
(
SELECT COUNT(*) as 'count'
FROM tbl
) AS t2
WHERE t1.row >= t2.count/2 and t1.row <= ((t2.count/2) +1)) AS t3;
RETURN medRslt;
END//
DELIMITER ;
I then proceed to execute the following query:
USE ap2;
SELECT vendor_id, calcMedian('invoices', 'invoice_total')
FROM invoices i
WHERE vendor_id = 97
GROUP BY vendor_id;
I get the error message:
SQL Error (1146): Table 'ap2.tbl' doesn't exist *
I understand that the following may be better off as stored procedure/prepared statement rather than function. I just want to take things one step at a time now.
Also I made a different function to simply output the value stored in the variable 'tbl', and it displayed the correct table name (invoices in this case).
Identifiers in a SQL statement cannot be provided as values. Identifiers (table names, column names, function names, etc.) must be specified in the SQL text.
To get the value of the tbl variable (procedure argument) used as a table name within a SQL statement in the procedure, you can use dynamic SQL.
Set a variable to the SQL text, incorporate the string value, and then execute the string as a SQL statement. As an example:
SET #sql = CONCAT( 'SELECT AVG(middle_values) AS `median`'
, ' INTO medRslt'
, ' ... '
, tbl
, ' ... '
);
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Be aware that incorporating string values into the SQL text makes the procedure subject to SQL Injection vulnerabilities.
If I had to do this, I would reduce the potential for SQL Injection by verifying that tbl does not contain a backtick character, and enclose/escape the identifier in backticks, e.g.
CONCAT( ' ...' , '`' , tbl , '`' , ' ... ' );
^^^ ^^^

#1064 MySql error in nested query statements

Here is the nested query i am using:
insert into ( select dbtable from profiles where ( email = 'blah#test.com' ) ) values ( null, null, 'blah#test.com', 'testing' )
the tablename will be fetched by the SELECT query for the INSERT query to run but i am getting this #1064 error.
I get another error of "#1103 - Incorrect table name" if i use back-ticks like this:
insert into `( select dbtable from profiles where ( email = 'blah#test.com' ) )` values ( null, null, 'blah#test.com', 'testing' )
I tried and searched for this but still i am stuck here. Can anybody help?
Sorry if this is an easy question as i am a newbie in this.
select dbtable from profiles where ( email = 'blah#test.com' ) into #tableName;
set #queryy = CONCAT(CONCAT('INSERT INTO ', #tableName), ' values ( null, null, \'blah#test.com\', \'testing\' )');
prepare myQuery from #queryy;
execute myQuery;
deallocate prepare myQuery;
I think it should help you solve your problem.

INSERT INTO ... SELECT without detailing all columns

How do you insert selected rows from table_source to table_target using SQL in MySQL where:
Both tables have the same schema
All columns should transfer except for the auto-increment id
Without explicitly writing all the column names, as that would be tedious
The trivial INSERT INTO table_target SELECT * FROM table_source fails on duplicate entries for primary key.
Either you list all of the fields you want in the insert...select, or you use something else externally to build the list for you.
SQL does not have something like SELECT * except somefield FROM, so you'll have to bite the bullet and write out the field names.
Column names have to be specified -
INSERT INTO table_target SELECT NULL, column_name1, column_name2, column_name3, ...
FROM table_source;
Just pass NULL as a value for the auto-increment id field.
Of course, primary key must be unique. It depends on what you want to achieve, but you could exclude rows with a primary key that already exists.
INSERT INTO table_target SELECT * FROM table_source
WHERE table_source.id NOT IN (SELECT id FROM table_target)
UPDATE: since you also need the extra rows, you should resolve the conflict first, does table_source have relationships? If not you could change those keys:
UPDATE table_source SET id = id + 1000
WHERE id IN (SELECT id FROM table_target)
Where 1000, is a constant, big enough so they go after the end of your table.
Tedious but safe and correct.
Writing INSERT statements without providing a list of columns leads to code that's hard to debug and, more importantly, very fragile code that will break if the definition of the table is changed.
If you absolutely can't write the column names out yourself then it's relatively easy to build a tool into your code that will create the comma-separated list for you.
This is my final solution to mass update with 'replace insert' command.
SET ##session.group_concat_max_len = ##global.max_allowed_packet;
SET #schema_db = 'db';
SET #tabl = 'table';
SET #cols = (SELECT CONCAT('`',GROUP_CONCAT(COLUMN_NAME SEPARATOR '`, `'), '`') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = #schema_db AND TABLE_NAME = #tabl GROUP BY TABLE_NAME);
SET #Querystr = CONCAT('REPLACE INTO ',#schema_db,'.',#tabl,' SELECT ', #cols,' FROM import.tbl_', #tabl);
PREPARE stmt FROM #Querystr;
EXECUTE stmt;
I think you could use syntax like:
INSERT INTO table (a,b,c) VALUES (1,2,3)
ON DUPLICATE KEY UPDATE c=c+1;
REF: http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html
Hope it helps
INSERT IGNORE just "bypass" the duplicate rows.
http://dev.mysql.com/doc/refman/5.5/en/insert.html
You can probably do it with prepared statements.
PREPARE table_target_insert FROM 'INSERT INTO table_target SELECT ? FROM table_source';
SET #cols:='';
SELECT #cols:=GROUP_CONCAT(IF(column_name = 'id','NULL',column_name) separator ",") FROM information_schema.columns WHERE table_name='table_source';
EXECUTE table_target_insert USING #cols;
It seems as if columns can not be given as a place holder in a MySQL Prepared Statement. I have compiled the following solution for testing:
SET #schema_db = 'DB';
SET #table = 'table';
SET #cols = (SELECT CONCAT(GROUP_CONCAT(COLUMN_NAME SEPARATOR ', '), "\n") FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = #schema_db AND TABLE_NAME = #table GROUP BY TABLE_NAME);
SET #Querystr = CONCAT('SELECT',' ', #cols,' ','FROM',' ',#schema_db,'.',#table,' ', 'Limit 5');
PREPARE stmt FROM #Querystr;
EXECUTE stmt;
You can use dynamic query:
DECLARE #Columns VARCHAR(MAX)=''
DECLARE #Query VARCHAR(MAX)=''
SELECT
#Columns = ISNULL(#Columns +',', '') + T.COLUMN_NAME
FROM
(
select name as COLUMN_NAME from sys.all_columns
where object_id = (select object_id from sys.tables where name = 'Source_Table')
and is_identity = 0
)T
set #Query = 'insert into Target_Table (' + SUBSTRING(#Columns,2 , 9999) + ') select ' + SUBSTRING(#Columns,2 , 9999) + ' from Source_Table';
PRINT #Query
EXEC(#Query)
The easiest way to do it is to use phpmyadmin to write the list of columns, then to change it as needed, in the example below I want to duplicate row with id=1078 and in this table I have id unique auto increment and alias unique.therefore I created my query as follow, with id & alias replaced by a desired value. and it worked like a charm.
INSERT INTO sy3_menuselect 1079, menutype, title, "alias", note, path, link, type, published, parent_id, level, component_id, checked_out, checked_out_time, browserNav, access, img, template_style_id, params, lft, rgt, home, language, client_id from sy3_menuwhere id=1078
Alternatively, to auto increment id, use the following Join statement:
INSERT INTO sy3_menuselect *
from (SELECT MAX(id+1 )from sy3_menu)a
join (select menutype, title, "alias", note, path, link, type, published, parent_id, level, component_id, checked_out, checked_out_time, browserNav, access, img, template_style_id, params, lft, rgt, home, language, client_idfrom sy3_menuwhere id=1079)b

MySQL sorting table by column names

I have already built a table with field names in arbitrary order. I want those field names to be in alphabetical order so that I can use them in my dropdown list. Is it possible with a query?
Select columns from a specific table using INFORMATION_SCHEMA.COLUMNS and sort alphabetically with ORDER BY:
SELECT column_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = '[schemaname]'
AND table_name = '[tablename]'
ORDER BY column_name
Note: The following code will alter the specified table and reorder the columns in alphabetical order
This should do the trick. It's a bit messy and lengthy, and you'll have to change the database name and table name, but for this one, the only requirement is that there is a database named "test" and that you are running these commands in it:
Let's create the tables we need:
-- CREATE TESTING TABLE IN A DATABASE NAMED "test"
DROP TABLE IF EXISTS alphabet;
CREATE TABLE alphabet (
d varchar(10) default 'dee' not null
, f varchar(21)
, e tinyint
, b int NOT NULL
, a varchar(1)
, c int default '3'
);
-- USE A COMMAND STORAGE TABLE
DROP TABLE IF EXISTS loadcommands;
CREATE TABLE loadcommands (
id INT NOT NULL AUTO_INCREMENT
, sqlcmd VARCHAR(1000)
, PRIMARY KEY (id)
);
Now let's create the two stored procedures required for this to work:
Separating them since one will be responsible for loading the commands, and including a cursor to immediately work with it isn't plausible (at least for me and my mysql version):
-- PROCEDURE TO LOAD COMMANDS FOR REORDERING
DELIMITER //
CREATE PROCEDURE reorder_loadcommands ()
BEGIN
DECLARE limitoffset INT;
SET #rank = 0;
SET #rankmain = 0;
SET #rankalter = 0;
SELECT COUNT(column_name) INTO limitoffset
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = 'test'
AND table_name = 'alphabet';
INSERT INTO loadcommands (sqlcmd)
SELECT CONCAT(t1.cmd, t2.position) AS commander FROM (
SELECT #rankalter:=#rankalter+1 AS rankalter, CONCAT('ALTER TABLE '
, table_name, ' '
, 'MODIFY COLUMN ', column_name, ' '
, column_type, ' '
, CASE
WHEN character_set_name IS NOT NULL
THEN CONCAT('CHARACTER SET ', character_set_name, ' COLLATE ', collation_name, ' ')
ELSE ' '
END
, CASE
WHEN is_nullable = 'NO' AND column_default IS NULL
THEN 'NOT NULL '
WHEN is_nullable = 'NO' AND column_default IS NOT NULL
THEN CONCAT('DEFAULT \'', column_default, '\' NOT NULL ')
WHEN is_nullable = 'YES' THEN 'DEFAULT NULL '
END
) AS cmd
, column_name AS columnname
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = 'test'
AND table_name = 'alphabet'
ORDER BY columnname
) t1
INNER JOIN (
SELECT #rankmain:=#rankmain+1 AS rownum, position FROM (
SELECT 0 AS rownum, 'FIRST' AS position
, '' AS columnname
UNION
SELECT #rank:=#rank+1 AS rownum, CONCAT('AFTER ', column_name) AS position
, column_name AS columnname
FROM INFORMATION_SCHEMA.COLUMNS
WHERE table_schema = 'test'
AND table_name = 'alphabet'
ORDER BY columnname
LIMIT limitoffset
) inner_table
) t2 ON t1.rankalter = t2.rownum
;
END//
DELIMITER ;
If anyone thinks/sees that I'm missing to include any important column attributes in the ALTER command, please hesitate not and mention it! Now to the next procedure. This one just executes the commands following the order of column id from the loadcommands table. :
-- PROCEDURE TO RUN EACH REORDERING COMMAND
DELIMITER //
CREATE PROCEDURE reorder_executecommands ()
BEGIN
DECLARE sqlcommand VARCHAR(1000);
DECLARE isdone INT DEFAULT FALSE;
DECLARE reorderCursor CURSOR FOR
SELECT sqlcmd FROM loadcommands ORDER BY id;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET isdone = TRUE;
OPEN reorderCursor;
read_loop:LOOP
FETCH reorderCursor INTO sqlcommand;
IF isdone THEN
LEAVE read_loop;
END IF;
SET #sqlcmd = sqlcommand;
PREPARE stmt FROM #sqlcmd;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END LOOP read_loop;
CLOSE reorderCursor;
END//
DELIMITER ;
The SQL is long, so if someone can point out ways (and has tested them) to make this shorter I'd gladly do it, but for now, this at least works on my end. I also didn't need to put dummy data in the alphabet table. Checking the results can be done using the SHOW... command.
The last part:
-- TO TEST; AFTER RUNNING DDL COMMANDS:
SHOW CREATE TABLE alphabet; -- SEE ORIGINAL ORDER
CALL reorder_loadcommands(); -- PREPARE COMMANDS
CALL reorder_executecommands(); -- RUN COMMANDS
SHOW CREATE TABLE alphabet; -- SEE NEW ORDER
Perhaps later on I could make reorder_loadcommands dynamic and accept table and schema parameters, but I guess this is all for now..