I am trying to get maximum length of each column of a table in Redshift database with this query:
select MAX(LEN(Trim(col1)))mx_len from tbl1;
and insert result into a tmp table like:
tbl1 col1 50
For this, I am trying to write a cursor in redshift to fetch column name one by one and insert data into tmp table. I am getting column names from following query:
select columnname from information_schema.columns where table_name = 'tbl1'
but unable to write a cursor, can anyone help me with this. Thanks in advance.
You haven't describes how you are trying to declare a cursor so it is a little hard to know what the problem is. So I'll describe a generic path to making a cursor and fetching from it.
First a cursor only lives for the duration of the transaction so you must begin and keep open a transaction.
BEGIN;
Next you need to declare the cursor and what data it will contain.
DECLARE <cursor_name> CURSOR FOR SELECT X, Y, Z FROM ...
Then you need to fetch a row from the cursor.
FETCH FORWARD 1 FROM <cursor_name>;
Then get the next row.
FETCH NEXT FROM <cursor_name>;
And on and on. When done close the cursor and end the transaction.
CLOSE <cursor_name>;
END;
Is this what you are doing? Is this not working?
Related
I'm working on a e-learning project in which there is a table named chapter in which there is a column named question_table this is table in which the specific chapter's questions are added.
Now the problem is I want to display all the question from all the chapter for this I used following sql query
SELECT * FROM (SELECT `question_table` FROM `chapter`)
but it doesn't work and gives the error:
"Every derived table must have its own alias".
Note: I want to do it using SQL not PHP.
Firstly, I think you would be better redesigning your database. Multiple tables of the same structure holding the same data are generally not a good idea.
However what you require is possible using a MySQL procedure to build up some dynamic SQL and then execute it, returning the resulting data.
A procedure as follows could be used to do this:-
DROP PROCEDURE IF EXISTS dynamic;
delimiter //
CREATE PROCEDURE dynamic()
BEGIN
DECLARE question_table_value VARCHAR(25);
DECLARE b INT DEFAULT 0;
DECLARE c TEXT DEFAULT '';
DECLARE cur1 CURSOR FOR SELECT `question_table` FROM `chapter`;
DECLARE CONTINUE HANDLER FOR NOT FOUND SET b = 1;
OPEN cur1;
SET b = 0;
WHILE b = 0 DO
FETCH cur1 INTO question_table_value;
IF b = 0 THEN
IF c = '' THEN
SET c = CONCAT('SELECT * FROM `',question_table_value, '`');
ELSE
SET c = CONCAT(c, ' UNION SELECT * FROM `',question_table_value, '`');
END IF;
END IF;
END WHILE;
CLOSE cur1;
SET #stmt1 := c;
PREPARE stmt FROM #stmt1;
EXECUTE stmt;
END
This is creating a procedure called dynamic. This takes no parameters. It sets up a cursor to read the question_table column values from the chapter table. It looks around the results from that, building up a string which contains the SQL, which is a SELECT from each table with the results UNIONed together. This is then PREPAREd and executed. The procedure will return the result set from the SQL executed by default.
You can call this to return the results using:-
CALL dynamic()
Down side is that this isn't going to give nice results if there are no rows to return and they are not that easy to maintain or debug with the normal tools developers have. Added to which very few people have any real stored procedure skills to maintain it in future.
In MySQL you must give every subquery ("derived table") an alias:
SELECT * FROM (SELECT question_table FROM chapter) t --notice the alias "t"
The derived table here is the result of the (SELECT ...). You need to give it an alias, like so:
SELECT * FROM (SELECT question_table FROM chapter) X;
Edit, re dynamic tables
If you know all the tables in advance, you can union them, i.e.:
SELECT * FROM
(
SELECT Col1, Col2, ...
FROM Chapter1
UNION
SELECT Col1, Col2, ...
FROM Chapter2
UNION
...
) X;
SqlFiddle here
To do this solution generically, you'll need to use dynamic sql to achieve your goal.
In general however, this is indicative of a smell in your table design - your chapter data should really be in one table, and e.g. classified by the chapter id.
If you do need to shard data for scale or performance reasons, the typical mechanism for doing this is to span multiple databases, not tables in the same database. MySql can handle large numbers of rows per table, and performance won't be an issue if the table is indexed appropriately.
I'm trying to figure out how to create a looped truncate to truncate (or delete) the data in specific tables only. There are something like 30 or so tables that need to be truncated, and I want to avoid just using a list of truncate statements. I can't seem to find any good examples of doing this, and no matter what I try, I get a "right truncated" error.
Example 1:
FOR anlyc_tables AS curs CURSOR FOR
SELECT table_name FROM systable WHERE table_name LIKE 'table_to_truncate_prefix%'
DO EXECUTE (
'TRUNCATE TABLE ' + table_name
);
END FOR;
This one throws no errors, but completes in .078 seconds and doesn't actually truncate anything.
Example 2:
ALTER PROCEDURE truncate_analytics()
BEGIN
DECLARE #table_name VARCHAR;
DECLARE curs DYNAMIC SCROLL CURSOR FOR SELECT table_name FROM systable WHERE table_name LIKE 't_anlyc%';
OPEN curs WITH HOLD;
FETCH NEXT curs INTO #table_name;
WHILE(sqlstate = 0) LOOP
FETCH NEXT curs INTO #table_name;
TRUNCATE TABLE table_name;
END LOOP;
END
GO
CALL truncate_analytics()
GO
Results in the "right truncated" error and the tables are not truncated.
I think I'm missing something really obvious here, but I don't have a ton of experience with SQL scripting in this manner, and can't seem to find any working examples of this to prove it's even possible.
Can anyone point me in the right direction?
There may be a few things at work that are preventing the code from working.
With your second example, the text "TRUNCATE TABLE table_name" is going to look for a table named table_name. table_name won't be treated for it's variable value, but will be considered a object identifier. FWIW, I'm not sure how this would cause a right truncation error.
Depending on what you consider the prefix for a table, you might not be getting the list of tables you expect. If you are considering the user to be the prefix (i.e. dba.my_table), your select isn't going to grab the necessary tables; there can be multiple tables with the same name, just different owners. If you want to select based on user, you'll need to join to the SYSUSER view.
Lastly, using a cursor for this can be done. It would require WITH HOLD as you already have, because the TRUNCATE TABLE statement issues an implicit commit and closes cursors otherwise. (Note that WITH HOLD requires explicit closing of the cursor, otherwise it will remain open until connection close.) Another option is to build a string of statements to execute, and use the execute or EXECUTE IMMEDIATE on them as a batch.
Hope This Helps,
Tyson
I want to create a table with a few records in it and then run a set of sql statements for every record in that table. I would use the data in the table to set values in the sql statement.
This should allow me to write the SQL just once and then run it for whatever data I put in the table.
But, I'm not sure how to go about doing this. Should I use a cursor to loop the table? Some other way?
Thanks for any help or advice you can give me.
CURSOR will have an overhead associated with it, but can be a good method to walk through your table. They are not a totally unnecessary evil and have their place.
With the limited information that WilliamB2 provided, it sounds like a CURSOR set may be a good solution for this problem to walk through his data and generate the multiple downstream INSERTs.
Yes you can use a cursor. You can also use a while loop
declare #table as table(col1 int, col2 varchar(20))
declare #col1 int
declare #col2 varchar(50)
declare #sql varchar(max)
insert into #table
SELECT col1, col2 FROM OriginalTable
while(exists(select top 1 'x' from #table)) --as long as #table contains records continue
begin
select top 1 #col1=col1, #col2=col2 from #table
SET #sql = 'INSERT INTO Table t VALUES('+cast(#col1 as varchar)+')'
delete top (1) from #table --remove the previously processed row. also ensures no infinite loop
end
I think cursor has an overhead attached to it.
With this second approach you are not working on the original table
Maybe you could use INSERT...SELECT instead of the loop:
INSERT INTO target_table
SELECT
some_col,
some_other_col,
'Some fixed value',
NULL,
42,
you_get_the_idea
FROM source_table
WHERE source_table.you_get_the_idea = 1
The columns on your SELECT should match the structure of the target table (you can omit an int/identity pk like id if you have one).
If the best option is this or the loop depends on how many tables you want to populate inside the loop. If it's just a few, I usually stick with INSERT...SELECT.
I am trying to create a sql cursor on a sum of sum.
CURSOR DATA_CURSOR IS
SELECT SUM(A)+SUM(B) FROM DATA_TABLE
I guess that may not be the correct way to do it.... how do I create it and how to fetch it?
You need to name the new column you created in the query e.g.:
CURSOR DATA_CURSOR IS
SELECT SUM(A)+SUM(B) AS SUM_AB
FROM DATA_TABLE
I would like to know how to remove a value from a Table Valued Parameter in SQL?
Is this even possible? If not, what would be the workaround?
Please see my code below and note the comments. The comments portion is where I would like to insert a line that would allow me to remove a record from the TVP #Record_NumList
Once that record is removed, I then proceed to insert a new one (basically, I don't want to execute the stored procedure on an existing record).
DECLARE #RecordID int
DECLARE #RecordID_NumList as Le_NumberList
DECLARE cur_DelRec CURSOR
FOR
SELECT DBRecordID from <<table_name>>
OPEN cur_DelRec
FETCH NEXT from cur_DelRec into #RecordID
INSERT into #RecordID_NumList(#RecordID)
WHILE ##FETCH_STATUS = 0
BEGIN
exec <<stored_procedure>> #RecordID_NumList
/* I'm stuck here: */
/* Need to remove from the existing record
from #RecordID_NumList */
FETCH NEXT FROM cur_DelRec into #RecordID
INSERT into #RecordID_NumList(#RecordID)
END
CLOSE cur_DelRec
DEALLOCATE cur_DelRec
On a more general level, is a TVP a collection? Let's say I chose not to delete it from #Record_NumList. What would happen in that case?
I apologize, but I'm still learning SQL and any help would be greatly appreciated!
Ray
Table valued parameters works just like a regular table. You should be able to just do something like this:
delete from #Record_numList where [field] = [value]