Best way to copy date from one table to some others - sql

I have this table:
ID
Name
Role
1
Tom
Admin
2
Tom
User
3
John
Admin
From this table I need to copy the data into the two tables like this:
User:
ID
Name
1
Tom
2
John
UserRole:
UserID
RoleID
1
Admin
1
User
2
Admin
How can I do it in a stored procedure?

You can use queries:
insert into user (name)
select distinct name
from following;
insert into userrole (userid, roleid)
select u.userid, f.roleid
from following f join
user u
on f.name = u.name;
These can be incorporated into a stored procedure.

you can also try merge add,update, delete. Merge uses the composite index or primary key to find matching rows. This is a data warehouse way of etl. you may have to set identity off for a primary key.
use MyDataDB
declare #Output as varchar(max)
declare #ColumnName as varchar(100)
declare #TableName as varchar(30)='your_table_name'
declare #UpdateStatement as varchar(max)=''
declare #InsertStatement as varchar(max)=''
declare #InsertStatement2 as varchar(max)=''
declare #DeleteStatement as varchar(max)=''
Declare #Join as varchar(max)
declare #JoinColumns Table(ColumnName varchar(50))
---- Add your primary Key here or composite index
Insert into #JoinColumns(ColumnName)
values('YourKeyID')
select #Join=
(select ' AND TARGET.'+ColumnName+'=SOURCE.'+ColumnName from #JoinColumns for xml path(''))
select #Join=right(#Join,len(#Join)-5)
declare c1 cursor
for
SELECT
COLUMN_NAME
FROM
INFORMATION_SCHEMA.COLUMNS
WHERE
TABLE_NAME = #TableName
open c1
fetch next from c1 into #ColumnName
set #Output=N'MERGE ReportData.dbo.'+ #TableName + ' as TARGET USING ReportData.dbo.' + #TableName + '_Stage as SOURCE ON
(' + #Join +') WHEN MATCHED THEN ';
set #UpdateStatement=N' UPDATE SET ';
set #InsertStatement =N' WHEN NOT MATCHED BY TARGET THEN INSERT(';
set #InsertStatement2=N' Values(';
set #DeleteStatement=N' WHEN NOT MATCHED BY SOURCE THEN DELETE';
while ##FETCH_STATUS=0
begin
if not exists(select '' from #JoinColumns where ColumnName=#ColumnName)
begin
if #UpdateStatement=N' UPDATE SET '
SET #UpdateStatement=#UpdateStatement + 'TARGET.' + #ColumnName+'='+'SOURCE.'+#ColumnName
else
SET #UpdateStatement=#UpdateStatement + ',TARGET.' + #ColumnName+'='+'SOURCE.'+#ColumnName
end
if #InsertStatement =N' WHEN NOT MATCHED BY TARGET THEN INSERT('
BEGIN
Set #InsertStatement=#InsertStatement+#ColumnName
Set #InsertStatement2=#InsertStatement2+'SOURCE.'+#ColumnName
END
ELSE
BEGIN
Set #InsertStatement=#InsertStatement+','+#ColumnName
Set #InsertStatement2=#InsertStatement2+',SOURCE.'+#ColumnName
END
fetch next from c1 into #ColumnName
end
close c1
deallocate c1
SET #Output=#Output + #UpdateStatement
SET #Output=#Output + #InsertStatement+') '
SET #Output=#Output + #InsertStatement2+') '
SET #Output=#Output + #DeleteStatement+';'
select #Output

Related

Create a new column as num containing values of the table name

I have two tables in SQL and want to create a new column from the table name.
In the database, there are many many tables and name are following similar pattern e.g. total_abc_001, total_abc_0002etc... I want only tables starting with total_abc to be selected..
And create a new column as num which contains the values of table name last characters like 001, 002.
I have tried like this:
DROP TABLE IF EXISTS TABLE_NEW_XY
CREATE TABLE TABLE_NEW_XY
(
email VARCHAR(MAX),
Profile VARCHAR(MAX)
)
DECLARE #Sql NVARCHAR(MAX) = '',
#TableName VARCHAR(MAX),
#Id INT
DECLARE Table_Cursor CURSOR FOR
SELECT
ROW_NUMBER() OVER (ORDER BY TABLE_NAME ASC) Id,
TABLE_NAME
FROM
INFORMATION_SCHEMA.TABLES
WHERE
TABLE_TYPE = 'BASE TABLE'
AND TABLE_NAME LIKE 'total_abc_%'
OPEN Table_Cursor
FETCH NEXT FROM Table_Cursor INTO #Id, #TableName
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#Id = 1)
BEGIN
SET #Sql = #Sql + 'SELECT email, Profile FROM '+#TableName
SELECT #SQL
END
ELSE
BEGIN
SET #Sql = #Sql + ' UNION ALL SELECT email, Profile FROM '+#TableName --Modify the columns based on your column names
END
FETCH NEXT FROM Table_Cursor INTO #Id,#TableName
END
CLOSE Table_Cursor
DEALLOCATE Table_Cursor
INSERT INTO TABLE_NEW_XY
EXEC (#Sql)
Now i wanted to add a new column "num in the dynamic sql itself to get the values of the filename in the column as 001,002,003 and so on.
Can you please suggest how to achieve this?
If the number is always 3 digits on the end of the #TableName you can let your dynamic sql hardcode it:
IF (#Id = 1)
BEGIN
SET #Sql = #Sql + 'SELECT email, Profile, ''' + RIGHT(#TableName,3) + ''' tname FROM '+#TableName
SELECT #SQL
END
ELSE
BEGIN
SET #Sql = #Sql + ' UNION ALL SELECT email, Profile, ''' + RIGHT(#TableName,3) + ''' tname FROM '+#TableName --Modify the columns based on your column names
END
This should output:
SELECT email, Profile, '001' tname FROM total_abc_001
UNION ALL SELECT email, Profile, '002' tname FROM total_abc_002
If the n last characters you want to capture are variable you can use the same principle with some more complex find/replace functions

Select a value from table for each database [duplicate]

I have (for testing purposes) many dbs with the same schema (=same tables and columns basically) on a sql server 2008 r2 instance.
i would like a query like
SELECT COUNT(*) FROM CUSTOMERS
on all DBs on the instance. I would like to have as result 2 columns:
1 - the DB Name
2 - the value of COUNT(*)
Example:
DBName // COUNT (*)
TestDB1 // 4
MyDB // 5
etc...
Note: i assume that CUSTOMERS table exists in all dbs (except master).
Try this one -
SET NOCOUNT ON;
IF OBJECT_ID (N'tempdb.dbo.#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp
(
[COUNT] INT
, DB VARCHAR(50)
)
DECLARE #TableName NVARCHAR(50)
SELECT #TableName = '[dbo].[CUSTOMERS]'
DECLARE #SQL NVARCHAR(MAX)
SELECT #SQL = STUFF((
SELECT CHAR(13) + 'SELECT ' + QUOTENAME(name, '''') + ', COUNT(1) FROM ' + QUOTENAME(name) + '.' + QUOTENAME(#TableName)
FROM sys.databases
WHERE OBJECT_ID(QUOTENAME(name) + '.' + QUOTENAME(#TableName)) IS NOT NULL
FOR XML PATH(''), TYPE).value('text()[1]', 'NVARCHAR(MAX)'), 1, 1, '')
INSERT INTO #temp (DB, [COUNT])
EXEC sys.sp_executesql #SQL
SELECT *
FROM #temp t
Output (for example, in AdventureWorks) -
COUNT DB
----------- --------------------------------------------------
19972 AdventureWorks2008R2
19975 AdventureWorks2012
19472 AdventureWorks2008R2_Live
Straight forward query
EXECUTE sp_MSForEachDB
'USE ?; SELECT DB_NAME()AS DBName,
COUNT(1)AS [Count] FROM CUSTOMERS'
This query will show you what you want to see, but will also throw errors for each DB without a table called "CUSTOMERS". You will need to work out a logic to handle that.
Raj
How about something like this:
DECLARE c_db_names CURSOR FOR
SELECT name
FROM sys.databases
WHERE name NOT IN('master', 'tempdb') --might need to exclude more dbs
OPEN c_db_names
FETCH c_db_names INTO #db_name
WHILE ##Fetch_Status = 0
BEGIN
EXEC('
INSERT INTO #report
SELECT
''' + #db_name + '''
,COUNT(*)
FROM ' + #db_name + '..linkfile
')
FETCH c_db_names INTO #db_name
END
CLOSE c_db_names
DEALLOCATE c_db_names
SELECT * FROM #report
declare #userdb_list table (name varchar(4000) not null);
-- fill the db list with custom subset
insert into #userdb_list
select name from sys.databases --can add where condition to filter db names
declare
#curr_userdb varchar(300),
#db_placeholder varchar(300),
#final_db_exec_query varchar(max),
#query varchar(max);
set #query = '' -- <add ur query here>
set #db_placeholder = 'use {db}';
set #curr_userdb = (select min(name) from #userdb_list);
while #curr_userdb is not null
begin
set #final_db_exec_query = replace(#db_placeholder, '{db}', #curr_userdb + ' ' + #query);
exec (#final_db_exec_query);
--print #final_db_exec_query
set #curr_userdb = (select min(name) from #userdb_list where name > #curr_userdb);
end
GO
Solution without cursor - clean and simple
Because I know that a question was just referred to here that asked a slightly different question... if you only want to execute on certain databases, those databases could be stored in some table. Here I stored in a temporary table.
CREATE TABLE #Databases (
DbName varchar(255))
INSERT INTO #Databases (DbName)
Values ('GIS_NewJersey'), ('GIS_Pennsylvania')
DECLARE #command varchar(1000)
SELECT #command = 'Use [' + DbName + '];
Update sde.SAP_Load
SET FullAddress = CONCAT_WS('','', HouseNumber, Street, City, Postal, RegionName)
Update sde.PREMISE
SET FullAddress = CONCAT_WS('', '', HouseNumber, Street, City, Postal, RegionName)
Update sde.PREMISE_GEOCODE
SET FullAddress = CONCAT_WS('', '', HouseNumber, Street, City, Postal, RegionName)'
FROM #Databases
EXEC #command

How to do a huge search for Primary Key ID's that is used across the database where these Primary Key ID's have similar values in columns

BackDrop: We are researching why a number of accounts were missed in a process. We have went back to as far as we have data. We now have a rather large list of accounts that for whatever reason were missed. Now this process without going into too much detail is VERY VERY complex and we need to know why these accounts and only these accounts were missed. As any DataBase we have many many automated procedures that run all the time, so there is really at this point no telling what on earth happened to cause these accounts to get missed. My only bet I think at solving this is to find similarities between these accounts. Obviously we have tried looking at the more common places and have since found nothing.
Issue: I want to use SQL to return all the tablenames and columnnames in our database Where these list of accounts have the same value in a column or columns of a table. I have created a query to find tablenames, columns, and so forth but dont know how to bring it all together to create one query that will give me all the results I want. I am certain that a cursor will need to be used and lots of inner joining but I am just not sure how this should be done.
Again:
Lets say I have account numbers 123456 and 654321 and I know our DataBase has 3,000 tables with a column reference to account number with a name of either AccountNumber, AccountNum, or Account. I want to search and find all tables that have a column with the name AccountNumber, AccountNum, or Account that has a value of 123456 or 654321. Then with these tables, for each table I want to take the rows Where the column whether the name be AccountNumber, AccountNum, or Account has a value of either 123456 and 654321 and then for each of those rows I want to check each column of each row to see if the columns on a row for account number 123456 is eqaul to a column on a row for account number 654321 , if so then I want it to return the column name and the tablename. This way I can see what these accounts have in common.
ADVANCED PORTION:
IF some poor soul is able to do the above then I'd also like to create a query that will return
The tablename and when it was updated. I would get the updated value by checking each column in each table and if the column has a type of "timestamp" or a default value of "GetDate()" then that column would be used as updated. In final result set that shows were all changes have happened for those account nubmers it will order by updated.
A first approach, rustic (I'm not that used to T-SQL, I did more PL/SQL), but which may help you going further, AND TESTED IN SQL SERVER 2008. Hope it works in 2005...)
So, we create two procedures, one calling the other
The provided code can only check, in one time
- for 2 differents IDs
- for all concerned fields (Account, AccountNum, AccountNumber)
The idea (checking for AccountNumber column)
Find the tables (in table INFORMATION_SCHEMA.columns, which lists your database tables) which have a column with one of the 3 names provided
For every table found :
create a dynamic query :
select count(*) from <THE_TABLE> where <Account_column_name> IN (123456 654321);
If we have 2 results (mean that our Ids are both present in table), we launch the second procedure, with parameters : #TableName = <THE_TABLE>, #FieldName = <Account_column_name>, #FirstId = 123456, #SecondId = 654321
We find the column names for <THE_TABLE> (again in INFORMATION_SCHEMA.columns).
For every column found :
create a dynamic query
select count(*) from <THE_TABLE> as T1
inner join <THE_TABLE> as T2 on T1.<COLUMN_NAME> = T2.<COLUMN_NAME>
where T1.<Account_column_name>= 123456
and T2.<Account_column_name>= 654321
if count(*) = 1, it means that the same value exists in the same column of the same table for the given ids.
In that case, we print <THE_TABLE> and <THE_COLUMN>
To launch search, in sql management studio, just make
EXEC GetSimilarValuesForFieldAndValue 123456, 654321
and in the "Messages" part, you should have a list of "results".
CREATE procedure [dbo].[GetSimilarValuesForFieldAndValue](#FirstId int, #SecondId int)
AS
DECLARE #sql nvarchar(MAX);
DECLARE #params NVARCHAR(MAX);
DECLARE #Count int;
DECLARE #Name NVARCHAR(MAX);
DECLARE #FieldName NVARCHAR(MAX);
DECLARE db_cursor CURSOR for
select TABLE_NAME, COLUMN_NAME FROM INFORMATION_SCHEMA.columns
where COLUMN_NAME IN('Account', 'AccountNumber', 'AccountNum');
OPEN db_cursor
FETCH next from db_cursor into #Name, #FieldName
while ##FETCH_STATUS = 0
begin
select #sql =
N' SELECT #Count=Count(*) FROM ' + #Name +
N' WHERE ' +#FieldName+' IN (#FirstId,#SecondId)'
SELECT #params = N'#FieldName NVARCHAR(MAX), #FirstId int, #SecondId int, #Count int out'
EXEC sp_executesql #sql, #params, #FieldName, #FirstId, #SecondId, #Count OUT
if (#Count = 2)
begin
exec dbo.CompareFields #Name, #FieldName, #FirstId, #SecondId
end
FETCH NEXT FROM db_cursor INTO #Name, #FieldName
end
close db_cursor
DEALLOCATE db_cursor
GO
The second one :
CREATE procedure [dbo].[CompareFields](#TableName NVARCHAR(MAX), #FieldName NVARCHAR(MAX), #FirstId int, #SecondId int)
as
DECLARE #ColumnName NVARCHAR(MAX)
DECLARE #Sql NVARCHAR(MAX)
DECLARE #Params NVARCHAR(MAX)
DECLARE #Count int
DECLARE cfCursor CURSOR FOR
SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS
where TABLE_NAME = ''+#TableName+' '
AND COLUMN_NAME <> ' '+#FieldName+''
OPEN cfCursor
FETCH next from cfCursor into #ColumnName
while ##FETCH_STATUS = 0
begin
select #Sql =
N' SELECT #Count = count(*) from ' +#TableName + ' T1 '+
N' INNER JOIN ' + #TableName + ' T2 ON T1.' +#ColumnName + ' = T2.' + #ColumnName +
N' WHERE T1.' +#FieldName + ' = '+ CAST(#FirstId as varchar) +
N' AND T2.' + #FieldName + ' = '+CAST(#SecondId as varchar)
SELECT #Params =
N'#TableName VARCHAR(MAX), #ColumnName VARCHAR(MAX), '+
N'#FieldName VARCHAR(MAX), #FirstId int, #SecondId int, #Count int out'
exec sp_executesql #sql, #Params, #TableName, #ColumnName, #FieldName, #FirstId, #SecondId, #Count OUT
if #Count = 1
begin
--print tableName and column Name if value is identic
print 'Table : ' + #TableName + ' : same value for ' + #ColumnName
end
FETCH NEXT FROM cfCursor INTO #ColumnName
end
close cfCursor
DEALLOCATE cfCursor
GO
I actually had to do this for Guids at one point. Here is the script for doing with Guids. One sec and I'll work on modifying it to suit your needs:
DECLARE #table VARCHAR(100)
DECLARE #column VARCHAR(100)
DECLARE #value INT
SET #value = '06B8BD6C-A8EC-4EB3-9562-6666EE86952D'
DECLARE table_cursor CURSOR
FOR select tbl.Name, cols.name as TableName FROM sys.columns cols JOIN
sys.tables tbl on cols.object_id = tbl.object_id
where system_type_id = 36
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #table, #column;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = 'SELECT ''' + #Table + ''' AS TBL,''' +
#column + ''' AS COL FROM [' + #table + ']
WITH(NOLOCK) WHERE ' + #column + ' = ''' + CAST(#value AS VARCHAR(50)) + ''''
print #sql
EXEC sp_executesql #Sql
FETCH NEXT FROM table_cursor
INTO #table, #column;
END
CLOSE table_cursor
DEALLOCATE table_cursor
Updated to handle for searching on a field name:
DECLARE #table VARCHAR(100)
DECLARE #column VARCHAR(100)
DECLARE #value UNIQUEIDENTIFIER
SET #value = --ENTER YOUR ACCOUNT NUMBER HERE
DECLARE table_cursor CURSOR
select tbl.Name, cols.name as TableName FROM sys.columns cols JOIN
sys.tables tbl on cols.object_id = tbl.object_id
where cols.Name = 'AccountNumber'
OR cols.Name = 'AccountNum' OR cols.Name = 'Account'
OPEN table_cursor
FETCH NEXT FROM table_cursor
INTO #table, #column;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #SQL NVARCHAR(1000)
SET #SQL = 'SELECT ''' + #Table + ''' AS TBL,''' + #column +
''' AS COL FROM [' + #table + '] WITH(NOLOCK)
WHERE ' + #column + ' = ''' + CAST(#value AS VARCHAR(50)) + ''''
print #sql
EXEC sp_executesql #Sql
FETCH NEXT FROM table_cursor
INTO #table, #column;
END
CLOSE table_cursor
DEALLOCATE table_cursor

How can I update all empty string fields in a table to be null?

I have a table with 15 columns ( 10 of them are string value) and about 50000 rows.
It contain a lot empty string value ... I search if is there a query that I can pass a table name for it to iterate all values and if it equal empty then update it to NULL ..
UPDATE mytable
SET col1 = NULLIF(col1, ''),
col2 = NULLIF(col2, ''),
...
this is a simple way to do it based on table. just pass the proc the table names. you can also make a sister proc to loop thought table names and call this proc inside the while loop to work on each table in your loop logic.
CREATE PROC setNullFields
(#TableName NVARCHAR(100))
AS
CREATE TABLE #FieldNames
(
pk INT IDENTITY(1, 1) ,
Field NVARCHAR(1000) NULL
);
INSERT INTO #FieldNames
SELECT column_name FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = #TableName
DECLARE #maxPK INT;
SELECT #maxPK = MAX(PK) FROM #FieldNames
DECLARE #pk INT;
SET #pk = 1
DECLARE #dynSQL NVARCHAR(1000)
WHILE #pk <= #maxPK
BEGIN
DECLARE #CurrFieldName NVARCHAR(100);
SET #CurrFieldName = (SELECT Field FROM #FieldNames WHERE PK = #pk)
-- update the field to null here:
SET #dynSQL = 'UPDATE ' + #TableName + ' SET ' + #CurrFieldName + ' = NULLIF('+ #CurrFieldName+ ', '''' )'
EXEC (#dynSQL)
SELECT #pk = #pk + 1
END

Cross Tab Query Required SQL 2000

I have already googled for this
I have a Table with following structure in SQL 2000
ID ContactName Designation
1 A CEO
2 B ABC
3 C DEF
4 D GHI
I need the Output as follows
ContactName1 Contactname2 ContactName3 ContactName4
A CEO B ABC C DEF D GHI
Any Suggestions ?
It occurs to me that a lot of the examples are for cross tab queries involving aggregation, which yours does not appear to need. While I do not necessarily condone Dynamic SQL, the below should give you the results you want.
Create table #Contacts (id int)
Declare #ContactTypes int
Declare #CAD varchar(100)
Declare #I int
Declare #sql nvarchar(4000)
Set #i = 1
Select #ContactTypes =
Sum(sub.Types)
from ( Select Count(1) as Types from contacts
group by ContactName, Designation) as sub
Print #ContactTypes
While #i <= #ContactTypes
Begin
set #sql = 'alter table #Contacts Add ContactName' +
Cast(#I as varchar(10)) + ' varchar(100)'
exec sp_executesql #sql
Set #I = #i + 1
End
Insert into #Contacts (id) values (1)
Set #i = 1
Declare crsPivot cursor
for Select ContactName + ' ' + Designation
from contacts
open crsPivot
Fetch next from crsPivot into #CAD
While (##Fetch_Status = 0)
Begin
Set #sql = 'Update #Contacts set ContactName'
+ Cast(#I as varchar(10)) +' = ' + quotename(#CAD,'''')
exec sp_executesql #sql
Set #i = #i + 1
Fetch next from crsPivot into #CAD
End
close crsPivot
Deallocate crsPivot
select * From #Contacts
You need to use a PIVOTED Table
This should work with 2000 also - wg. without PIVOT
http://www.sqlteam.com/item.asp?ItemID=2955
Yet another SQL Cross Tab proc http://johnmacintyre.ca/codespct.asp