I wanted to throw this out there for some ideas. I'm writing a program to generate insert/update statements, and I want the table that I insert/update to come from the results of a query. So something like (please forgive the syntax):
INSERT INTO (SELECT TBL_NAME FROM MYTABLES WHERE A=B) VALUES ('A', 'B', 'C');
I have to do this in Oracle, but I'm not too familiar with their declare statements or syntax. I'm guessing the best way to go about it is to declare a variable that is the result of the SELECT, but then can I use that variable as the table name for the INSERT?
I also want to keep the code in SQL.
Thanks for any ideas.
I think you may want to look into Dynamic SQL, you may find your answer (or at least a decent starting path) there.
How about something like this:
SELECT 'INSERT INTO ' || TBL_NAME || ' VALUES (''A'', ''B'', ''C'');' cmd
FROM MYTABLES WHERE A=B
;
Run this select, then run the results of the select (which is insert statements).
Don't forget to "commit".
Regards,
Roger
All views are mine ...
Related
My SQL is a bit rusty, so I don't know whether the following is even possible:
I have multiple tables t_a, t_b, t_c with the same column layout and I want to apply the same operation to them, namely output some aggregation into another table. For a table t_x this would look like this:
CREATE TABLE t_x_aggregate (
<here the col definitions which are the same for all new tables t_[abc]_aggregate>
);
INSERT INTO t_x_aggregate(id, ...)
SELECT id, SUM(factor*amount)
FROM t_x
WHERE some fixed condition
GROUP BY id;
I now want to execute something like a FOR loop around this:
for t_x in t_a, t_b, t_c
CREATE TABLE ...
INSERT INTO ...
end for
Is this possible in SQL? Or would I need to build a wrapper in another language for this?
So, the result of that operation would be 3 new tables? T_A_AGGREGATE, T_B_AGGREGATE and T_C_AGGREGATE?
I think that the fastest way is to write 3 separate CREATE TABLE statements, e.g.
create table t_a_aggregate as
select id, sum(factor * amount) suma
from t_a
where some_condition
group by id;
create table t_b_aggregate as
select id, sum(factor * amount) suma
from t_b
where some_condition
group by id;
create table t_c_aggregate as
select id, sum(factor * amount) suma
from t_c
where some_condition
group by id;
OK; I understand that queries aren't that simple, but nothing much changes - only table names in CREATE and FROM (perhaps somewhere else, but that's more or less "it"). Any decent text editor's search/replace capabilities should be able to do it quickly.
If you want to do it dynamically in a loop (read: PL/SQL), you can - but dynamic SQL doesn't scale, is difficult to maintain, is painful to debug. Therefore, if you're doing it only once, consider running 3 separate statements.
How to do it dynamically?
You'd have to create a string (we usually put them into a locally declared variable) which contains the whole DDL statement. Why? Because you can't execute DDL from a PL/SQL otherwise.
If there are multiple tables and/or columns involved, you'll have to combine "fixed" parts of the statement (like create table, select, from, order by) concatenated with "dynamic" parts - such as column names. Note that in between you have to concatenate commas as separators. Pay attention to usage of multiple single quotes as you have to escape them (or use the q-quoting mechanism).
Also, for multiple columns you'll probably have to do it in a loop, concatenating each new column to previously composed string.
It (the statement stored into the varirable) is the executed by EXECUTE IMMEDIATE. If it is correctly written, it'll succeed. Otherwise, it'll fail, but it won't tell you why it failed (that's why I said difficult debugging").
So, instead of executing it, we usually display that string (using dbms_output.put_line) so that we see how it looks like and - using copy/paste - try to execute it.
Basically, it can be quite complex and - as I said - difficult to maintain and debug.
For the FOR loop you need to use PL/SQL like this:(*)
declare
type array_t is table of varchar2(10);
array array_t := array_t('a', 'b', 'c');
lo_stmt varchar2(2000);
begin
lo_stmt :=
'CREATE TABLE t_'||array(i)||'_aggregate ('||
' <here the col definitions which are the same for all new tables t_[abc]_aggregate>'||
');'||
''||
'INSERT INTO t_'||array(i)||'_aggregate(id, ...)'||
'SELECT id, SUM(factor*amount)'||
'FROM t_'||array(i)||
'WHERE some fixed condition'||
'GROUP BY id;'||
execute immediate lo_stmt;
end loop;
end;
/
Look also at this SO question: How to use Oracle PL/SQL to create...
(*) #Littlefoot describes in the 2nd part of his answer valuable background to this program.
In SQL sometimes it is easier and faster to use table variables.
I know I can't use insert to table var in HANA DB, but what would be the best practice to do something similar?
I tried using SELECT to populate the variable but I can't insert multiply rows.
Do I have to use temporary table instead?
I would like to have a table with some values I create, like the below example I use for SQL, such a way I can use it later in the query:
Declare #temp table(Group Int, Desc nvarchar(100))
insert into #temp (Group , Desc )
Values (1,'Desc 1'), (2,'Desc2 2'), (3,'Desc 3'), (4,'Desc 4'), (5,'Desc 5')
In HANA, I am able to create the variable, but not able to populate it with multiple rows :(
Is there any best way to do so?
Thank you so much.
The "UNION"-approach is one option to add records to the data that a table variable is pointing to.
Much better than this is to either use arrays to add, remove, and modify data and finally turn the arrays into table variables via the UNNEST function. This is an option that has been available for many years, even with HANA 1.
Alternatively, SAP HANA 2 (starting with SPS 03, I believe), offers additional SQLScript commands, to directly INSERT, UPDATE, and DELETE on table variables. The documentation covers this in "Modifying the Content of Table Variables".
Note, that this feature comes with a slightly different syntax for the DML commands.
As of SAP HANA 2 SPS 04, there is yet another syntax option provided for this:
"SQL DML Statements on Table Variables".
This one, finally, looks like "normal" SQL DML against table variables.
Given these options, the "union"-approach is the last option you should use in your coding.
For who else try to find about it, I found a "work around" that is to use the UNION ALL.
I add the first row, then I do a UNION on the table and ADD the second line, like below:
tempTable = Select 1 as "Group", 'Desc' as "Desc" FROM DUMMY;
tempTable = SELECT "Group", "Desc" FROM :AcctClassificacao UNION ALL Select 2 as "Group",
'Desc' as "Desc" FROM DUMMY ;
Select * from tempTable
In this case, I will have the result:
Group Desc
1 Desc
2 Desc
I don't know if this is the best way to do anyways.
While contemplating this question about a SQL INSERT statement, it occurred to me that the distinction in syntax between the two statements is largely artificial. That is, why can't we do:
INSERT INTO MyTable SET Field1=Value1, Field2=Value2, ...
or
UPDATE MyTable ( Field1, Field2 ...) VALUES ( Value1, Value2, ... )
WHERE some-key = some-value
Perhaps I'm missing something critical. But for those of us who have had to concatenate our SQL statements in the past, having comparable syntax for an INSERT and an UPDATE statement would have saved a significant amount of coding.
They're serving different grammatical functions. In an update you are specifying a filter that chooses a set of rows to which you will apply an update. And of course that syntax is shared with a SELECT query for the same purpose.
In an INSERT you are not choosing any rows, you are generating a new row which requires specifying a set of values.
In an UPDATE, the LHS=RHS stuff is specifying an expression which yields true or false (or maybe null :) and in an INSERT, the VALUES clause is about assignment of value. So while they are superficially similar, they are semantically quite different, imho. Although I have written a SQL parser, so that may influence my views. :)
SQL Server 2008 has introduced UPSERT functionality via the MERGE command. This is the logical equivalent of
IF FOUND THEN
UPDATE
ELSE
INSERT
I believe this is so that you may make an insert statement without being explicit about the values. If you are putting a value in every single column in the table you can write:
insert into my_table values ("value1", 2);
instead of:
insert into my_table (column1, column2) values ("value1", 2);
When importing and exporting entire (large) databases, this is invaluable for cutting down file size and processing time. Nowadays, with binary snapshots and the like, it may be "less invaluable" :-)
I have a MSSQL 2005 database with a lot of records that were added since last backup. I want to make another SQL script that puts result values into string representing INSERT statement that I will save for later use.
Something like:
SELECT 'Insert INTO tabname columns VALUES("+Column1"',')' FROM XY
Or simple example:
Column A,Row1=5
SELECT A+"BLAH" FROM X
should return "BLAH5"
Thank you
I'm not sure i understand, if you want to build a script (lets say PHP) just run over the records and either print out or to file something like:
echo 'INSERT INTO tablename (field1,field2) VALUES('.$row['field1'].','.$row['field2'].');';
if you want that string in the result directly from the SQL you could use CONCAT:
SELECT CONCAT('INSERT INTO...VALUES(',field1,',',field2,')') FROM yourtable;
Hope that helps...
You should really mention what SQL database system you're using.
For MySQL, what you want is the CONCAT function.
SELECT CONCAT('INSERT INTO table (columns) VALUES ("', column1, '");') FROM xy;
what version of sql?
for ms sql, you can use + for concatenation and single quotes for strings
for mysql/oracle, use concat(column, 'string')
The INSERT syntax I've been using is this
INSERT INTO TableName VALUES (...)
The UPDATE syntax I've been using is
UPDATE TableName SET ColumnName=Value WHERE ...
So in all my code, I have to generate 2 strings, which would result in something like this
insertStr = "(27, 'John Brown', 102)";
updateStr = "ID=27, Name='John Brown', ItemID=102";
and then use them separately
"UPDATE TableName SET " + updateStr + " WHERE ID=27 " +
"IF ##ROWCOUNT=0 "+
"INSERT INTO TableName VALUES (" + insertStr + ")"
It starts bothering me when I am working with tables with like 30 columns.
Can't we generate just one string to use on both INSERT and UPDATE?
eg. using insertStr above on UPDATE statement or updateStr on INSERT statement, or a whole new way?
I think you need a whole new approach. You are open to SQL Injection. Provide us with some sample code as to how you are getting your data inputs and sending the statements to the database.
alt text http://goose.ycp.edu/~weddins/440/S09%20IFS440%20Bobby%20Drop%20Tables.PNG
As far as I'm aware, what you're describing isn't possible in ANSI SQL, or any extension of it that I know. However, I'm mostly familiar with MySQL, and it likely depends completely upon what RDBMS you're using. For example, MySQL has "INSERT ... ON DUPLICATE KEY UPDATE ... " syntax, which is similar to what you've posted there, and combines an INSERT query with an UPDATE query. The upside is that you are combining two possible operations into a single query, however, the INSERT and UPDATE portions of the query are admittedly different.
Generally, this kind of thing can be abstracted away with an ORM layer in your application. As far as raw SQL goes, I'd be interested in any syntax that worked the way you describe.
Some DBMS' have an extension to do this but why don't you just provide a function to do it for you? We've actually done this before.
I'm not sure what language you're using but it's probably got associative arrays where you can wrote something like:
pk{"ID"} = "27"
val{"Name"} = "'John Brown'"
val{"ItemID"} = "102"
upsert ("MyTable", pk, val)
and, if it doesn't have associative arrays, you can emulate them with multiple integer-based arrays of strings.
In our upsert() function, we just constructed a string (update, then insert if the update failed) and passed it to our DBMS. We kept the primary keys separate from our other fields since that made construction of the update statement a lot easier (primary key columns went in the where clause, other columns were just set).
The result of the calls above would result in the following SQL (we had a different check for failed update but I've put your ##rowcount in for this example):
update MyTable set
Name = 'John Brown',
ItemID = 102
where ID = 27
if ##rowcount=0
insert into MyTable (ID, Name, ItemID) values (
27,
'John Brown',
102
)
That's one solution which worked well for us. No doubt there are others.
Well, how about no statements? You might want to look into an ORM to handle this for you...
Some databases have proprietary extensions that do exactly this.
I agree that the syntax of INSERT and UPDATE could be more consistent, but this is just a fact of life now -- it ain't gonna change now. For many scenarios, the best option is your "whole new way": use an object-relational mapping library (or even a weak-tea layer like .NET DataSets) to abstract away the differences, and stop worrying about the low-level SQL syntax. Not a viable option for every application, of course, but it would allow you to just construct or update an object, call a Save method and have the library figure out the SQL syntax for you.
If you think about it, INSERT and UPDATE are exactly the same thing. They map field names to values, except the UPDATE has a filter.
By creating an associative array, where the key is the field name and the value is the value you want to assign to the field, you have your mapping. You just need to convert it to a the proper string format depending on INSERT or UPDATE.
You just need to create a function that will handle the conversion based on the parameters given.
SQL Server 2008:
MERGE dbo.MyTable AS T
USING
(SELECT
#mykey AS MyKey
#myval AS MyVal
) AS S
ON (T.MyKey = S.MyKey)
WHEN MATCHED THEN
UPDATE SET
T.MyVal = S.MyVal
WHEN NOT MATCHED THEN
INSERT (MyKey, MyVal)
VALUES (S.MyKey, S.MyVal)
MySQL:
INSERT (MyKey, MyVal)
INTO MyTable
VALUES({$myKey}, {$myVal})
ON DUPLICATE KEY UPDATE myVal = {$myVal}