Maybe there's a better way about this but this has to be somewhat dynamic.
From a vb.net form I need to restore or replace data from one table to another. The two tables are identical except for a couple different columns.
First I wrote some SQL to grab the column names of the table passed in. Then through ordinal position I get only the tables I want values from. I store these tables names in a temp table.
Now I want to get those values from the backup table using the temp table column names and place them in the master table.
So I guess I suppose I need a cursor to loop through in some way.. I haven't touched a cursor since college and wow.
I'll embarrass myself and post my current code.
SET #getColCURSOR = CURSOR FOR
SELECT name
FROM #MyTempTable --created previously as table only holding column names
OPEN #getColCURSOR
FETCH NEXT FROM #getColCURSOR
INTO #columnName
WHILE ##FETCH_STATUS = 0
BEGIN
select #columnName --this variable should as a column name and change
from AUDIT_TABLE a where a.ID = 7 -- 7 is just for testing is dynamic variable
FETCH NEXT FROM #getColCURSOR
INTO #columnName
END
CLOSE #getColCURSOR
DEALLOCATE #getColCURSOR
I'm not going to comment on whether this could be done without a cursor, since I'm a bit lost on what you're trying to do. But one issue with your cursor is that you can't parameterize a column name in a select statement. So you'll need to replace this:
WHILE ##FETCH_STATUS = 0
BEGIN
select #columnName --this variable should as a column name and change
from AUDIT_TABLE a where a.ID = 7 -- 7 is just for testing is dynamic variable
FETCH NEXT FROM getColCURSOR
INTO #columnName
END
--with dynamic SQL like this:
DECLARE #SQL nvarchar(max)
WHILE ##FETCH_STATUS = 0
BEGIN
set #SQL =
N'select ' + QUOTENAME(#columnName) + ' from AUDIT_TABLE a where a.ID = 7'
EXEC (#SQL); --w/o brackets assumes you've calling a stored proc
FETCH NEXT FROM getColCURSOR
INTO #columnName
END
That could possibly introduce other issues, since dynamic SQL statements execute in their own scope. I'd definitely encourage you to look into whether there's a set-based solution to this, since using dynamic SQL will make this even messier, and I don't think you'll be able to escape dynamic SQL if you want to use a variable for column names.
Related
I need to check that all primary key columns do have all values in uppercase.
So, I have a first request which returns me the table-field pairs which are part of PK.
SELECT table_name, field_name FROM dico WHERE pkey > 0;
(dico is some table which gives that information. No need to look it up in the SQL Schema…)
And, for all those pairs tx/fx listed from that first query above, I need to look for values which would not be uppercased.
SELECT DISTINCT 't1', 'f1', f1 FROM t1 WHERE f1 <> UPPER(f1) UNION ALL
SELECT DISTINCT 't2', 'f2', f2 FROM t2 WHERE f2 <> UPPER(f2) UNION ALL
...
SELECT DISTINCT 'tn', 'fn', fn FROM tn WHERE fn <> UPPER(fn);
(I'm putting the table name and field name as "strings" in the output, so that I know from where the wrong values are coming.)
As you see, I do have the code for both requests, but I do not know how to combine them (if possible, in a generic way that would work for both SQL Server and Oracle).
Can you give me some idea on how to finish that?
One way that I could think of is to use a statement block that contains a loop.
Unfortunately, the structure of a statement block will be different for every different database system (the one for SQL Server will be different for Oracle).
I wrote an example using SQL Server further below (fiddle link is at: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=85cd786adf32247da1aa73c0341d1b72).
Just in case, the dynamic query gets very long (possibly longer than the limit of varchar, which is 8000 characters), SQL Server has varchar(max) that can hold up to 2GB (https://learn.microsoft.com/en-us/sql/t-sql/data-types/char-and-varchar-transact-sql?view=sql-server-ver15). This can be used for #DynamicQuery, replacing VARCHAR(3000) in the example below (modified/alternative fiddle link, just to show that the data type really exists and can be used, is at: https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=7fbb5d130aad35e682d8ce7ffaf09ede).
Please note that the example is not using your exact queries because I do not have access to the exact same data as the one you have (e.g. I cannot test the example using dico table because I do not have access to that table).
However, I made the example so that it uses a similar basic structure of logic from your queries, so that later on it can be customised to suit your exact need/scenario (e.g. by changing the table names and field names to match the ones that you use, as well as by adding the WHERE clause as you need).
In the example, your 1st query will be run immediately and the result will be handled by a cursor.
After that, a loop (using WHILE statement/structure) will loop through the cursor for the result of the 1st query to dynamically build the 2nd query (inserting the table names and the field names from the 1st query).
Note that at this point, the 2nd query is still being built, not being run yet.
Eventually, after the loop has finished, the resulting/compiled 2nd query will be run/executed (using the EXEC command).
-- START of test data creation.
create table TableA
( message varchar(200)
);
insert into TableA([message]) values ('abc');
insert into TableA([message]) values ('def');
create table TableB
( message varchar(200)
);
insert into TableB([message]) values ('ghi');
insert into TableB([message]) values ('jkl');
-- END of test data creation.
-- START of dynamic SQL
declare #TableAndFieldDetails CURSOR
declare #TableName VARCHAR(50)
declare #FieldName VARCHAR(50)
declare #DynamicQuery VARCHAR(3000) = ''
begin
SET #TableAndFieldDetails = CURSOR FOR
-- START of the 1st query
SELECT [INFORMATION_SCHEMA].COLUMNS.TABLE_NAME,
[INFORMATION_SCHEMA].COLUMNS.COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE INFORMATION_SCHEMA.COLUMNS.COLUMN_NAME LIKE '%message%'
-- END of the 1st query
-- START of dynamically building the 2nd query
OPEN #TableAndFieldDetails
FETCH NEXT FROM #TableAndFieldDetails INTO #TableName, #FieldName
WHILE ##FETCH_STATUS = 0
BEGIN
IF #DynamicQuery <> ''
BEGIN
SET #DynamicQuery += ' UNION ALL '
END
-- The one line right below is each individual part/element of the 2nd query
SET #DynamicQuery += 'SELECT ''' + #TableName + ''', ''' + #FieldName + ''', ' + #FieldName + ' FROM ' + #TableName
FETCH NEXT FROM #TableAndFieldDetails INTO #TableName, #FieldName
END
CLOSE #TableAndFieldDetails
DEALLOCATE #TableAndFieldDetails
-- END of dynamically building the 2nd query
EXEC (#DynamicQuery)
end
-- END of dynamic SQL
I have a stored procedure where I need to query a table that contains another query that I then need to execute, get the results, and store those results in another table. I will not know what, or how many, columns are returned from this query, but I must be able to map the unknown columns to columns in my results table. I do know that the query can contain anywhere from 1 to 20 columns which need to map to my results table as RSLT_1 up to RSLT_20.
For Example, let's say the query returns 5 columns. I need to iterate over the results and map column1 to RSLT_1, column2 to RSLT_2, etc. Then store those results in my result table
I have this logic already written in C# which was trivial since I can loop over columns to determine how many exist. I don't know how to do that in a stored procedure. Any ideas?
I cannot write you a complete answer, because I don't know your queries/tables. Here main lines of what I'm thinking about. You have to complete for your needs. But keep in mind, cursor is slow and generally not recommended in production.
select * into #tmp from <sourceQuery>
declare #columnName varchar(100)
declare #sql NVARCHAR(MAX)
declare c cursor for
select name from tempdb.sys.columns where object_id = object_id('tempdb..#tmp')
open c
fetch next from c into #columnName
while ##FETCH_STATUS = 0
begin
select #columnName
set #sql = N'insert into <targetTable> (valueColumn) select ' + #columnName + 'from #tmp'
exec #sql
fetch next from c into #columnName
end
close c
deallocate c
drop table #tmp
I need a small help. I want to convert my result of SQL into single row.
Lets say there is a table Students with ID and Name in it.
if I execute query
select * from Students
it returns.
Col1 Col2
1 Rizwan
2 Ahmed
I want result to be like
1 Rizwan 2 Ahmed
Please note that I want every record in a separate column.
Thanks in advance
I can't think of a plausible scenario where this transform serves any useful purpose because relational-algebra is, by design, about sets of data sharing the same attributes (i.e. tables have rows with columns) - by having everything in a single row with meaningless columns you're just effectively serializing data into a blob.
The only way to achieve this is using Dynamic SQL, as this is the only way to achieve a dynamic number of columns without prior knowledge of what columns are desired.
In MS SQL Server you might think of using PIVOT/UNPIVOT but the columns still need to be manually named, thus requiring Dynamic SQL.
MySQL Server (and MariaDB) have GROUP_CONCAT which can be used to combine multiple rows into a single string (text) value but the server lacks any kind of "split" function. So GROUP_CONCAT doesn't work here because it doesn't return discrete columns.
In T-SQL (MS SQL Server, Sybase) you need to iterate over every target row, this is done using a CURSOR. You cannot reliably perform string concatenation inside a SELECT statment:
DECLARE #sql nvarchar(max) = 'SELECT '
DECLARE c CURSOR FOR
SELECT [Id], [Name] FROM Students ORDER BY [Id] ASC
OPEN c
DECLARE #id int
DECLARE #name nvarchar(100)
FETCH NEXT FROM c INTO #id, #name
WHILE ##FETCH_STATUS = 0
BEGIN
SET #sql = #sql + CONVERT( varchar(10), #id ) + ', ' + #name
FETCH NEXT FROM c INTO #id, #name
END
CLOSE c
DEALLOCATE c
sp_executesql #sql -- this will execute the `SELECT` that was generated, where each discrete value will be returned as an anonymous column.
I've searched here and elsewhere, and haven't found an answer yet. Hope I didn't miss it.
Using SQL Server Management Studio 2008 R2.
I have n specific databases on my server (there are other DBs as well, but I'm only interested in some of them)
Each of these databases has a table within it, which all have the same name. The only difference is the DB name. I want to aggregate these tables together to make one big table on a different database (different to the other DBs).
I can get the db names from the results of a query.
N is unknown.
Is a loop the way to go about this?
I was thinking something along the lines of the following pseudocode:
Set #dbnames = SELECT DISTINCT dbname FROM MyServer.dbo.MyTable
For each #name in #dbnames
INSERT INTO ADifferentDB.dbo.MyOtherTable
SELECT * FROM #name.dbo.table
Next name
(Clearly I'm new to using SQL variable as well, as you can see)
Your first problem is about iterating the databases: you cand do that with a cursor
Then you have another problem, executing a query where part of it is variable (database's name). You can do that with execute function.
All that is something similar to this:
DECLARE #query VARCHAR(max)
DECLARE #dbname VARCHAR(100)
DECLARE my_db_cursor CURSOR
FOR SELECT DISTINCT dbname FROM MyServer.dbo.MyTable
OPEN my_db_cursor
FETCH NEXT FROM my_db_cursor
INTO #dbname
WHILE ##FETCH_STATUS = 0
BEGIN
SET #query = 'INSERT INTO ADifferentDB.dbo.MyOtherTable
SELECT * FROM ' + #dbname + '.dbo.table'
EXECUTE(#query)
FETCH NEXT FROM my_db_cursor
INTO #dbname
END
CLOSE my_db_cursor
DEALLOCATE my_db_cursor
what you want to do is define a CURSOR for row-level operation. here is some doc
I would suggest using sp_MSForEachDB:
EXEC sp_MSForEachDB '
-- Include only the databases you care about.
IF NOT EXISTS (
SELECT *
FROM MySever.dbo.MyTable
WHERE dbname = ''?''
)
-- Exit if the database is not in your table.
RETURN
-- Otherwise, perform your insert.
INSERT INTO ADifferentDB.dbo.MyOtherTable
SELECT * FROM ?.dbo.table
'
In this case, ? is a token that is replaced with each database on the server.
Here's what I'd like to do.
For each table in linkedserver.database whose tablename is like 'text%'
(inside loop)
A. If current_table exists locally, drop it
B. select * into table.name (local) from linkedserver.tablename (copy
schema + data)
C. Possibly check for errors and Print some text about it?
Next
Any idea if this script is possible? I'm very clueless about working with table names if it would be possible to
select * into #Variable_Storing_Table_Name
from LinkedServer.DB.#Variable_Storing_Table_Name
Well, here's how to do this using a cursor:
use database
go
declare #link_table nvarchar(255)
declare #local_table nvarchar(255)
declare table_list cursor for
select
tlink.name,
tlocal.name
from
linkedserver.database.sys.tables tlink
left outer join sys.tables tlocal on
tlink.name = tlocal.name
open table_list
fetch next from table_list into #link_table, #local_table
while ##FETCH_STATUS = 0
begin
begin try
if #local_table is not null
begin
sp_executesql N'drop table ' + quotename(#local_table)
end
sp_executesql N'select * into ' + quotename(#link_table) +
' from linkedserver.database..' + quotename(#link_table)
print #link_table + ' copied.'
end try
begin catch
print 'Error: ' + ERROR_MESSAGE()
end catch
fetch next from table_list into #link_table, #local_table
end
close table_list
deallocate table_list
While cursors should generally be avoided, here you're looking to do a lot of logic behind each and every row. So, here it is. What it does is grab all of the linked tables and match any of the local tables to those, or null if the local table doesn't exist. This places it in a cursor, which we can use to iterate through the rowset.
The fetch next command grabs the next row from our cursor and then applies your logic to it (drop it if the local table exists, then do a select * into...).
You can catch errors one of two ways. I used the try...catch block, but you can also check ##ERROR and see if it's not equal to zero. Really, whatever you feel most comfortable with.
As a disclaimer for the anti-cursor crowd: Cursors aren't evil, they're just often used improperly.
There is an undocumented SQL Server function called sp_foreachtable that might do what you want. I'm not sure if it works on linked databases though... a Web search might turn something up.