Insertion in MySQL takes a lot of time - mysql
I have a lot of tables and few tables have around 20 million records.
Now I do some calculations on these tables before showing the result to the UI.
For that I have created stored procedure.
In the stored procedure I am using temprorary tables to store select query records, do join, do processing and then return result.
I have written query like this
Insert INTO A
SELECT * from B JOIN c ....
Now my select query here does not take time (I have optimize it using explain extended) but my Insert into takes a lot of time since the output of select is in million. My table A is temprorary.
And Also I have do some processing on this A table after that.
My query is is there a way where I can skip inserting this insert.
I can take the result in a seperate table variable(if any) and then do processing instead of inserting all that in temprorary and then again doing some processing on it.
Adding my stored procedure here
DROP PROCEDURE IF EXISTS 6_4_1n2_PortUtil_temp;
DELIMITER |
CREATE PROCEDURE 6_4_1n2_PortUtil_temp(utilType VARCHAR(100),service varchar(5000),p_networkType VARCHAR(20),inputCity varchar(5000),inputNodeName varchar(5000), startTime timestamp,endTime timestamp)
BEGIN
DROP TEMPORARY TABLE IF EXISTS TEMP_SERVICE_TBL;
DROP TEMPORARY TABLE IF EXISTS TEMP_NODEIF_TBL;
DROP TEMPORARY TABLE IF EXISTS TEMP_NODEIF_TBL_1;
DROP TEMPORARY TABLE IF EXISTS TEMP_NODEName_TBL;
DROP TEMPORARY TABLE IF EXISTS TEMP_NODEANDIF_TBL;
DROP TEMPORARY TABLE IF EXISTS TEMP_ROUTERTRAFFIC_VLANPRT_SCALE1_TBL;
DROP TEMPORARY TABLE IF EXISTS TEMP_TRAFFIC_TBL;
DROP TEMPORARY TABLE IF EXISTS TRAFFIC_TBL;
DROP TEMPORARY TABLE IF EXISTS FINAL_FETCH;
SELECT now();
CREATE TEMPORARY TABLE TEMP_SERVICE_TBL(nodeName varchar(256),NodeNumber int) ENGINE INNODB;
CREATE TEMPORARY TABLE TEMP_NODEName_TBL(nodeName varchar(256),NodeNumber int) ENGINE INNODB;
CREATE TEMPORARY TABLE TEMP_NODEIF_TBL(NodeNumber int,IfIndex INTEGER,IfSpeed FLOAT,IfDescr VARCHAR(100),IfAlias VARCHAR(100)) ENGINE INNODB;
CREATE TEMPORARY TABLE TEMP_NODEIF_TBL_1(NodeNumber int,IfIndex INTEGER,IfSpeed FLOAT,IfDescr VARCHAR(100),IfAlias VARCHAR(100)) ENGINE INNODB;
CREATE TEMPORARY TABLE TEMP_NODEANDIF_TBL(NodeName Varchar(256),NodeNumber int,IfIndex INTEGER,IfSpeed FLOAT,IfDescr VARCHAR(100),IfAlias VARCHAR(100),PortID BIGINT(20) DEFAULT 0) ENGINE INNODB;
CREATE TEMPORARY TABLE TRAFFIC_TBL(
PortID BIGINT(20),
NodeName VARCHAR(100),
IfDescr VARCHAR(100),
IfSpeed VARCHAR(100),
InErrPkts BIGINT(20),
RcvOctets BIGINT(20),
TxOctets BIGINT(20),
Time_1 TIMESTAMP) ENGINE INNODB;
CREATE TEMPORARY TABLE TEMP_TRAFFIC_TBL(
PortID BIGINT(20),
maxTrafficValueIn BIGINT(20) DEFAULT 0,
maxOutTrafficValueOut BIGINT(20) DEFAULT 0,
avgTrafficValueIn BIGINT(20) DEFAULT 0,
avgTrafficValueOut BIGINT(20) DEFAULT 0,
CRCError BIGINT(20) DEFAULT 0,
UpTime INTEGER DEFAULT 0,
Reliablity INTEGER DEFAULT 0,
AvgUtilIn float DEFAULT 0,
AvgUtilOut float DEFAULT 0,
PeakUtilIn float DEFAULT 0,
PeakUtilOut float DEFAULT 0,
ThresholdExceed INTEGER DEFAULT 0,
inPeakTime timestamp DEFAULT '0000-00-00 00:00:00',
outPeakTime timestamp DEFAULT '0000-00-00 00:00:00') ENGINE INNODB;
SET #where = '';
IF service='ALL'
THEN
SET #where = '';
ELSE
set #a=1;
set #like="";
select REPLACE(SUBSTRING(SUBSTRING_INDEX(service, ',', #a),LENGTH(SUBSTRING_INDEX(service, ',', #a -1)) + 1),',','') into #service;
while(#service != "")
DO
IF(#like = "")
THEN
SET #like = CONCAT("NodeName like '%",SUBSTRING(#service,2,3),"%'");
ELSE
SET #like = CONCAT(#like," or NodeName like '%",SUBSTRING(#service,2,3),"%'");
END IF;
set #a=#a+1;
select REPLACE(SUBSTRING(SUBSTRING_INDEX(service, ',', #a),LENGTH(SUBSTRING_INDEX(service, ',', #a -1)) + 1),',','') into #service;
END WHILE;
SET #where = CONCAT(" where",#like);
END IF;
Set #where2 = '';
IF inputCity='ALL'
THEN
set #where2 = '';
ELSE
set #where2 = CONCAT(' and substring(NodeName,1,3) in (',inputCity,')');
END IF;
SET #where3 = '';
IF inputNodeName='ALL'
THEN
set #where3 = '';
ELSE
set #where3 = CONCAT(' and NodeName in (',inputNodeName,')');
END IF;
SET #query1 := CONCAT("INSERT INTO TEMP_NODEName_TBL SELECT distinct NodeName,NodeNumber from NODE_TBL",#where, #where2, #where3);
SELECT #query1;
PREPARE statement1 from #query1;
EXECUTE statement1;
DEALLOCATE Prepare statement1;
CREATE INDEX n1 ON TEMP_NODEName_TBL(NodeNumber);
CREATE INDEX i1 ON TEMP_NODEIF_TBL(NodeNumber,IfIndex);
CREATE INDEX portIDIndex1 on TEMP_NODEANDIF_TBL(PortID);
SET #where4 = '';
IF (utilType='ALL')
THEN
SET #where = '';
ELSE
If (utilType = "'AESI-IN'")
THEN
SET #where4 = " where IfAlias like '%AESI-IN%'";
ELSE
SET #where4 = " where IfAlias NOT like '%AESI-IN%'";
END IF;
END IF;
CREATE INDEX i2 ON TEMP_NODEIF_TBL_1(NodeNumber,IfIndex);
CREATE INDEX i3 ON TEMP_NODEANDIF_TBL(NodeNumber,IfIndex);
SET #where5 = '';
IF(p_networkType != 'ALL')
THEN
set #r1= SUBSTRING(p_networkType,2,3);
set #r2= SUBSTRING(p_networkType,8,3);
set #r3= SUBSTRING(p_networkType,14,3);
SET #where5 = CONCAT(" and IfAlias like '%",#r1,"%'");
if(#r2 != "")
THEN
SET #where5 = CONCAT(" and IfAlias like '%",#r2,"%'");
IF(#r3 != "")
THEN
SET #where5 = CONCAT(" and IfAlias like '%",#r3,"%'");
END IF;
END IF;
END IF;
SET #query2 := CONCAT("INSERT INTO TEMP_NODEANDIF_TBL(NodeName,NodeNumber,IfIndex,IfSpeed,IfDescr,IfAlias) SELECT distinct b.NodeName, a.NodeNumber, a.IfIndex,a.IfSpeed,a.IfDescr,a.IfAlias from NODEIF_TBL a JOIN TEMP_NODEName_TBL b ON a.NodeNumber = b.NodeNumber ", #where4, #where5);
SELECT #query2;
PREPARE statement1 from #query2;
EXECUTE statement1;
DEALLOCATE Prepare statement1;
SELECT "DROP TEMPORARY TABLES";
DROP TEMPORARY TABLE IF EXISTS TEMP_NODEIF_TBL;
DROP TEMPORARY TABLE IF EXISTS TEMP_NODEIF_TBL_1;
DROP TEMPORARY TABLE IF EXISTS TEMP_NODEName_TBL;
update TEMP_NODEANDIF_TBL a,VLANPRT_TBL b set a.PortID = PrtID where a.NodeNumber = b.NodeID and a.IfIndex = b.IfIndex;
SELECT "Update Temporary tables";
delete from TEMP_NODEANDIF_TBL where PortID = 0;
SELECT now();
INSERT INTO TRAFFIC_TBL
SELECT a.PortID,NodeName,IfDescr,IfSpeed,InErrPkts,RcvOctets,TxOctets,Time_1 FROM ROUTERTRAFFIC_VLANPRT_SCALE1_TBL a JOIN TEMP_NODEANDIF_TBL b ON a.PortID = b.PortID where Time_1>startTime and Time_1<endTime;
SELECT now();
DROP TEMPORARY TABLE IF EXISTS EXCEED_COUNT;
CREATE TEMPORARY TABLE EXCEED_COUNT (PortID BIGINT(20), Exceed INTEGER);
INSERT INTO EXCEED_COUNT
select PortID,count(RcvOctets) from TRAFFIC_TBL where (RcvOctets/(IfSpeed*10)>70 or TxOctets/(IfSpeed*10)>70) group by PortID;
INSERT INTO TEMP_TRAFFIC_TBL (PortID, maxTrafficValueIn,maxOutTrafficValueOut,avgTrafficValueIn,avgTrafficValueOut,CRCError,AvgUtilIn,AvgUtilOut,PeakUtilIn,PeakUtilOut)
SELECT PortID,max(RcvOctets),max(TxOctets),avg(RcvOctets),avg(TxOctets), sum(InErrPkts),
IF((IfSpeed=0),"0",(avg(RcvOctets)/(IfSpeed*10))),
IF((IfSpeed=0),"0",(avg(TxOctets)/(IfSpeed*10))),
IF((IfSpeed=0),"0",(max(RcvOctets)/(IfSpeed*10))),
IF((IfSpeed=0),"0",(max(TxOctets)/(IfSpeed*10)))
from TRAFFIC_TBL group by PortID;
CREATE INDEX portIDIndex2 on TEMP_TRAFFIC_TBL(PortID);
CREATE INDEX portIDIndex3 on EXCEED_COUNT(PortID);
UPDATE TEMP_TRAFFIC_TBL A JOIN TRAFFIC_TBL B ON A.PortId=B.PortId SET inPeakTime=B.Time_1 where B.RcvOctets=A.maxTrafficValueIn;
UPDATE TEMP_TRAFFIC_TBL A JOIN TRAFFIC_TBL B ON A.PortId=B.PortId SET outPeakTime=B.Time_1 where B.TxOctets=A.maxOutTrafficValueOut;
UPDATE TEMP_TRAFFIC_TBL A JOIN EXCEED_COUNT B ON (A.PortID = B.PortID)
set ThresholdExceed = Exceed;
SELECT substring(NodeName,5,3) as ServiceType,
CASE
WHEN IfAlias like '%SWH%' THEN "Trunk"
WHEN IfAlias like '%AES%' THEN "Backbone"
WHEN IfAlias like '%RTR%' THEN "Back-to-Back"
ELSE "-"
END ,
NodeName,IfDescr,ROUND(maxTrafficValueIn/1000,2),ROUND(maxOutTrafficValueOut/1000,2),ROUND(avgTrafficValueIn/1000,2),ROUND(avgTrafficValueOut/1000,2),ROUND(CRCError/1000,2),0,0,ROUND(AvgUtilIn,2),ROUND(AvgUtilOut,2),ROUND(PeakUtilIn,2),ROUND(PeakUtilOut,2),ThresholdExceed,inPeakTime,outPeakTime from TEMP_TRAFFIC_TBL a ,TEMP_NODEANDIF_TBL b where a.PortID = b.PortID ;
SELECT now();
END |
DELIMITER ;
My ROUTERTRAFFIC_VLANPRT_SCALE1_TBL contains around 20 million records and NodeIF_TBL around 1 lakhs records. My VLANPRT_TBL also contains around 1 lakh records.
Also I have BTree indexing set on time_1 so that the join on the big table do not take time
Temporary tables:
Temporary tables start out in the memory and if the surpass a certain limitation they will be written to disk. If the select yields millions and you're writing those millions to disk then that's your problem.
Needing a temporary table with millions of records is usually a sign of doing something wrong. Perhaps you should further filter the data before inserting it into the temporary table for processing.
Disk I/O thrashing:
We have a few INSERT cases:
INSERT INTO ... VALUES
INSERT INGORE INTO ... SELECT
INSERT INTO ... SELECT
For 1. each insert has its own transaction and writes to disk individually meaning lots of tiny I/O operations.
For 2. the same applies except the data is also being read from the disk (slightly more I/O operations).
For 3. the whole operation is one big transaction meaning one big disk I/O at the very end (the commit) of the transaction. More often it happens that the information contained in the big transaction cannot be held entirely in memory so it is written to disk temporarily before the final commit and then copied from there into the designated area where it should reside.
In all 3 cases disk I/O thrashing occurs, to prevent this you would group small operations into average sized operations, and break huge operations into average sized operations.
For 1. you need to wrap every N inserts in a transaction.
For 2. and 3. you need to LIMIT the SELECT clause to N and repeat the INSERT INTO ... SELECT with an added OFFSET of N until you finish all records.
Either should be fairly easy to do if you're using a scripting language to run your queries.
Its not needed to store in a temporary table the output from a query in order to use it in another query. You can make this:
Select t1.* from (select * from A where condition1)as t1 where condition2
Here is an example of a query i had to do.
select avg(v1) as v1avg ,start_date_time as time, timekey as time_key from (select ATable.*, ROUND(UNIX_TIMESTAMP(start_date_time)/(60*60)) as timekey from ATable where start_date_time between '2014-01-01 00:00:00' and '2014-01-10 00:00:00')as t1 group by timekey;
Related
Insert into a table from multiple tables which are alike in Pattern
I have 1900 tables like table_1, table_2 and ........... which structure are same. I want to insert all data to a new table table_all. I know using UNION ALL I can do that but that's no useful as number of tables are large.
I made a procedure and it's working fine. DROP PROCEDURE IF EXISTS insert_from_multiple_table; CREATE PROCEDURE insert_from_multiple_table() Begin DECLARE tableCount INT DEFAULT 0; SELECT COUNT(*) INTO tableCount FROM information_schema.TABLES where table_name like 'table_%' and table_Schema='your_db'; WHILE tableCount>0 DO SET #dt_ncv = CONCAT("table_",tableCount); SET #cntstmt = CONCAT( "insert into alltable select * FROM ",#dt_ncv); #select #cntstmt; PREPARE ncv_count_stmt FROM #cntstmt; EXECUTE ncv_count_stmt; SET tableCount = tableCount-1; END WHILE ; END
How to `SELECT FROM` a table that is a part of a query itself using MySQL?
Say, if I have multiple tables that have the same schema: CREATE TABLE `tbl01` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `name` TINYTEXT, `data` INT ); CREATE TABLE `tbl02` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `name` TINYTEXT, `data` INT ); CREATE TABLE `tbl03` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `name` TINYTEXT, `data` INT ); -- etc. ------------------ INSERT INTO `tbl01` (`name`, `data`) VALUES ('row 1', 1), ('row 2', 1), ('row 3', 3); INSERT INTO `tbl02` (`name`, `data`) VALUES ('cube', 1), ('circle', 0); INSERT INTO `tbl03` (`name`, `data`) VALUES ('one', 1); and then one table that contains names of all other tables in one of its columns: CREATE TABLE `AllTbls` ( `id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `tblnm` VARCHAR(64) NOT NULL UNIQUE, `desc` TINYTEXT, `flgs` BIGINT UNSIGNED ); INSERT INTO `AllTbls` (`tblnm`, `desc`, `flgs`) VALUES ('tbl01', 'Table 1', 0), ('tbl02', 'Table two', 1), ('tbl03', '3rd table', 0); So if I want to write a query to retrieve contents of AllTbls and also in one column to include count of rows in each of corresponding tables, I thought the following would be the way to do it: SELECT *, `tblnm` as TblName, (SELECT COUNT(*) FROM TblName) as cntRws FROM `AllTbls` ORDER BY `id` ASC LIMIT 0,30; But this returns an error: #1146 - Table 'database.TblName' doesn't exist I know that I can do this in multiple queries (using a loop in a programming language), but is it possible to do it in one query? PS. I'm using MySQL v.5.7.28
The simple answer is: "you can't" Table names are not supposed to be used like variables, to hold data, in this way. What you're supposed to have is one table: tblContractCounts Client, ContractCount ------------------- IBM, 1 Microsoft, 3 Google, 2 Not three tables: tblIBMContractCounts ContractCount 1 tblMicrosoftContractCounts ContractCount 3 tblGoogleContractCounts ContractCount 2 If your number of tables is known and fixed you can perhaps remedy things by creating a view that unions them all back together, or embarking on an operation to put them all into one table, with separate views named the old names so things carry in working til you can change them. If new tables are added all the time it's a flaw in the data modelling and need to be corrected. In that case you'd have to use a programming language (front end or stored procedure) to build a single query: //pseudo code strSql = "" for each row in dbquery("Select name from alltbls") strSql += "select '" + row.name + "' as tbl, count(*) as ct from " + row.name + " union all " next row strSql += "select 'dummy', 0" result = dbquery(strSql) It doesn't have to be your front end that does this - you could also do this in mysql and leverage the dynamic sql / EXECUTE. See THIS ANSWER how we can concatenate a string using logic like above so that the string contains an sql query and then execute the query. The information schema will give you the info you need to get a list of all current table names But all you're doing is working around the fact that your data modelling is broken; I recommend to fix that instead ps: the INFORMATION_SCHEMA has rough counts for tables with their names, which may suffice for your needs in this particular case select table_name, table_rows from infornation_schema.tables where table_name like ...
I managed to solve the problem using the following stored procedure. -- DROP PROCEDURE sp_Count_Rows; Delimiter $$ CREATE PROCEDURE sp_Count_Rows() BEGIN DECLARE table_name TEXT DEFAULT ""; DECLARE finished INTEGER DEFAULT 0; DECLARE table_cursor CURSOR FOR SELECT tblnm FROM alltbls; DECLARE CONTINUE HANDLER FOR NOT FOUND SET finished = 1; OPEN table_cursor; DROP TABLE IF EXISTS RowsCount; CREATE TABLE IF NOT EXISTS RowsCount(Tlbnm text, ctnRws int); table_loop: LOOP FETCH table_cursor INTO table_name; IF finished = 1 THEN LEAVE table_loop; END IF; SET #s = CONCAT("insert into RowsCount select '", table_name ,"', count(*) as cntRws from ", table_name); PREPARE stmt1 FROM #s; EXECUTE stmt1; DEALLOCATE PREPARE stmt1; END LOOP table_loop; CLOSE table_cursor; SELECT * FROM RowsCount; DROP TABLE RowsCount; END $$ And then when you call the procedure CALL sp_Count_Rows(); You get this result
MySQL / Mariadb Stored Procedure, Prepared Statement, Union, Values from dynamically created tables and column names
I'd like to create reports without having to create a pivot table in excel for every report. I have survey software that creates a new table for each survey. The columns are named with ID numbers. So, I never know what the columns will be named. The software stores answers in two different tables depending on the 'type' of question. (text, radio button, etc.) I manually created a table 'survey_answers_lookup' that stores a few key fields but it duplicates the answers. The procedure 'survey_report' works well and produces the required data but there is a challenge. Since the survey tables are created when someone creates a new survey, I would need a trigger on the schema that creates a second trigger and I don't think that is possible. The second trigger would monitor the survey table and insert the data into the 'survey_answers_lookup' table after someone completes a survey. I could edit the php software and insert the values into the survey_answers_lookup table but that would create more work when I update the software. (I'd have to update the files and then put my changes back in the files). I also could not determine where they insert the values into the tables. Can you please help? Edited. I posted my solution below.
Change some_user to a user who has access to the database. CREATE DEFINER=`some_user`#`localhost` PROCEDURE `usp_produce_survey_report`(IN survey_id VARCHAR(10), IN lang VARCHAR(2)) SQL SECURITY INVOKER BEGIN /*--------------------------------------------------------------------------------- I do not guarantee that this will work for you or that it cannot be hacked with with SQL injections or other malicious intents. This stored procedure will produce output that you may use to create a report. It accepts two arguments; The survey id (745) and the language (en). It parses the column name in the survey table to get the qid. It will copy the answers from the survey table to the survey_report table if the answer is type S or K. It will get the answers from the answers table for other types. NOTE: Other types might need to be added to the if statement. Additionally, the qid and id from the survey table are also copied to the survey_report table. Then the questions from the questions table, and answers from the answers and survey_report tables are combined and displayed. The data in the survey_report table is deleted after the data is displayed. The id from the survey table is displayed as the respondent_id which may be used to combine the questions and answers from a specific respondent. You may have to change the prefix on the table names. Example: survey_answers to my_prefix_answers. Use this to call the procedure. Syntax: call survey.usp_produce_survey_report('<SURVERY_ID>', '<LANGUAGE>'); Example: call survey.usp_produce_survey_report('457345', 'en'); use this to create the table that stores the data CREATE TABLE `survey_report` ( `id` int(11) NOT NULL AUTO_INCREMENT, `qid` int(11) NOT NULL DEFAULT '0', `survey_row_id` int(11) NOT NULL DEFAULT '0' COMMENT 'id that is in the survey_<id> table', `answer` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL, PRIMARY KEY (`id`) ); */ DECLARE v_col_name VARCHAR (25); DECLARE v_qid INT; DECLARE v_col_count INT DEFAULT 0; DECLARE done INT DEFAULT false; DECLARE tname VARCHAR(24) DEFAULT CONCAT('survey_survey_',survey_id); DECLARE counter INT DEFAULT 0; DECLARE current_row INT DEFAULT 0; DECLARE total_rows INT DEFAULT 0; -- select locate ('X','123457X212X1125', 8); -- use locate to determine location of second X - returns 11 -- select substring('123457X212X1125', 11+1, 7); -- use substring to get the qid - returns 1125 DECLARE cur1 cursor for SELECT column_name, substring(column_name, 11+1, 7) as qid -- get the qid from the column name. the 7 might need to be higher depending on the id. FROM information_schema.columns -- this has the column names WHERE table_name = tname -- table name created form the id that was passed to the stored procedure AND column_name REGEXP 'X'; -- get the columns that have an X DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; SET done = FALSE; OPEN cur1; SET total_rows = (SELECT table_rows -- get the number of rows FROM INFORMATION_SCHEMA.TABLES WHERE table_name = tname); -- SELECT total_rows; read_loop: LOOP FETCH cur1 INTO v_col_name, v_qid; -- v_col_name is the original column name and v_qid is the qid that is taken from the column name IF done THEN LEAVE read_loop; END IF; -- SELECT v_col_name, v_qid; SET counter = 1; -- use to compare id's SET current_row = 1; -- used for the while loop WHILE current_row <= total_rows DO SET #sql := NULL; -- SELECT v_col_name, v_qid, counter, x; -- SELECT counter as id, v_col_name, v_qid as qid, x; -- SET #sql = CONCAT ('SELECT id ', ',',v_qid, ' as qid ,', v_col_name,' FROM ', tname, ' WHERE id = ', counter ); -- I would have to join the survey table below if I did not add the answer (v_col_name). I assume this is faster than another join. SET #sql = CONCAT ('INSERT INTO survey_report(qid,survey_row_id,answer) SELECT ',v_qid, ',id,' , v_col_name, ' FROM ', tname, ' WHERE id = ', counter ); -- SELECT #sql; PREPARE stmt FROM #sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; -- SELECT counter, x; SET current_row = current_row + 1; -- increment counter for while loop SET counter = counter + 1; -- increment counter for id's END WHILE; END LOOP; -- read_loop CLOSE cur1; -- SELECT * FROM survey_report -- ORDER BY id, qid; SET #counter = 0; SELECT #counter:=#counter + 1 AS newindex, -- increment the counter that is in the header survey_report.id, survey_report.survey_row_id as respondent_id, -- the id that copied from the survey table survey_report.qid, question, IF(type IN ('S' , 'K'), (SELECT answer FROM survey_report WHERE qid NOT IN (SELECT qid FROM survey_answers) AND survey_questions.language = lang AND survey_report.id = #counter), (SELECT answer FROM survey_answers WHERE survey_questions.qid = survey_answers.qid AND survey_report.qid = survey_questions.qid AND survey_report.answer = survey_answers.code AND survey_answers.language = lang ) ) AS answer FROM survey_questions JOIN survey_report ON survey_report.qid = survey_questions.qid WHERE survey_questions.sid = survey_id ORDER BY survey_report.survey_row_id, survey_report.id; TRUNCATE TABLE survey_report; END
referring to the already fetched results in a query within the same query
So suppose I have this table with 4 columns: id content parent timestamp whereby the parent column refers to an id of another entry in the table I want to accomplish the following: Select the first 50 rows from the table Ordered by the following: for each row, if(parent = 0){ add row to resultset, ordered by timestamp } else if (parent != 0){ if parent is in the list of rows already fetched so far by the query, add row to resultset, ordered by the timestamp otherwise, wait until the parent gets fetched by the query (assuming it gets fetched at all since there we're only getting the first 50 rows) } this ordering logic is somewhat complicated, and I'm wondering if it's even possible to accomplish this using MYSQL ORDER BY statement in a single query WITHOUT having to resort to subqueries? Perhaps we could set and use variables? But how would the ORDER BY statement will be implemented?
Here's a solution using variables which I incorporated into a procedure with a loop using an outer join and a temporary table to hold the unused inputdata but no subqueries. Delete any old version of the procedure and set the delimiter DROP PROCEDURE IF EXISTS order_proc; DELIMITER ;; Start writing the procedure CREATE PROCEDURE order_proc() BEGIN DECLARE n INT DEFAULT 50; DECLARE m INT DEFAULT 0; DECLARE i INT DEFAULT 0; DECLARE custom_limit INT DEFAULT 0; DROP TABLE IF EXISTS pre_resultset; CREATE TABLE pre_resultset LIKE input_data; ALTER TABLE pre_resultset MODIFY id INT; ALTER TABLE pre_resultset DROP PRIMARY KEY; ALTER TABLE pre_resultset ADD COLUMN iid INT PRIMARY KEY NOT NULL AUTO_INCREMENT FIRST; SELECT n INTO custom_limit; Set SQL_SELECT_LIMIT = custom_limit; INSERT INTO pre_resultset SELECT NULL,id, content, parent, timestamp FROM input_data WHERE parent = 0 ORDER BY timestamp; Set SQL_SELECT_LIMIT = default; DROP TABLE IF EXISTS unused_input_data; CREATE TABLE unused_input_data LIKE input_data; ALTER TABLE unused_input_data ADD null_col VARCHAR(1); SELECT COUNT(*) FROM pre_resultset INTO m; WHILE m<n DO SELECT COUNT(*) FROM pre_resultset INTO m; TRUNCATE unused_input_data; INSERT INTO unused_input_data SELECT input_data.id, input_data.content, input_data.parent, input_data.timestamp, pre_resultset.id AS null_col FROM input_data LEFT OUTER JOIN pre_resultset on input_data.id = pre_resultset.id WHERE pre_resultset.id IS NULL ; SELECT n-m INTO custom_limit; Set SQL_SELECT_LIMIT = custom_limit; INSERT INTO pre_resultset SELECT NULL, unused_input_data.id, unused_input_data.content, unused_input_data.parent, unused_input_data.timestamp FROM unused_input_data JOIN pre_resultset WHERE unused_input_data.parent = pre_resultset.id ORDER BY unused_input_data.timestamp; Set SQL_SELECT_LIMIT = default; SELECT COUNT(*) FROM pre_resultset INTO i; SELECT (IF( i = m, n, i)) INTO m; END WHILE; SELECT id, content, parent, timestamp FROM pre_resultset AS resultset; DROP TABLE IF EXISTS pre_resultset; DROP TABLE IF EXISTS unused_input_data; End; ;; Change the delimeter back DELIMITER ; Run the procedure CALL order_proc();
How to declare Internal table in MySQL?
I want to know how to define or declare Internal table in MySQL I am new to MySQL and i Don't know the syntax as you can see I create Stored Procedure CREATE DEFINER=`root`#`localhost` PROCEDURE `MySP`( actioncode VARCHAR(5), TNewsID BIGINT ) BEGIN IF actioncode = 1 then -- Retrive all from the database -- select * from emp.tbnews; elseIF actioncode = 2 then -- Retrive all from the database By NewsID -- select NewsID,NewsSubject,NewsSubjectAR,NewsDetails,NewsDetailsAR,CreatedOn,DisplayOrder, AllowDisplay,img from emp.tbnews Where NewsID=TNewsID; elseIF actioncode = 3 then -- fkjskldfjklsdf -- select NewsID,NewsSubject,NewsSubjectAR,NewsDetails,NewsDetailsAR,CreatedOn,DisplayOrder, AllowDisplay,img from emp.tbnews; END IF; END What I Really want is to declare Internal table before the IF Statement in Sql Server I am doing it like this declare #tbTemp table ( a as int, b as char...etc. ) because i want to put insert statement after IF actioncode = 1 Insert into #tbTemp so please if you know tell me how best regards for every one.
create temporary table tmp ( id int unsigned not null, name varchar(32) not null ) engine=memory; -- change engine type if required e.g myisam/innodb insert into tmp (id, name) select id, name from foo... ; -- do more work... select * from tmp order by id; drop temporary table if exists tmp; or create temporary table tmp engine=memory select id, name from foo... ; -- do more work... select * from tmp order by id; drop temporary table if exists tmp;
Do you mean CREATE TEMPORARY TABLE ? It's an in-memory table specific to the connection running the statement so unless you are running persistent connections you have no worries on name conflicts.