Create view or SP, only if the DB contains a pattern - sql

I am working on a script, that needs to be run in many different SQL servers. Some of them, shared the same structure, in other words, they are identical, but the filegroups and the DB names are different. This is because is one per client.
Anyway, I would like when running a script, If I chose the wrong DB, it should not be executed. I am trying to mantain a clean DB. here is my example, which only works for dropping a view if exists, but does not work for creating a new one. I also wonder how it would be for creating a stored procedure.
IF EXISTS (SELECT *
FROM dbo.sysobjects
WHERE id = object_id(N'[dbo].[ContentModDate]')
AND OBJECTPROPERTY(id, N'IsView') = 1)
AND CHARINDEX('Content', DB_NAME()) > 0
DROP VIEW [dbo].[ContentModDate]
GO
IF (CHARINDEX('Content', DB_NAME()) > 0)
BEGIN
CREATE VIEW [dbo].[Rx_ContentModDate] AS
SELECT 'Table1' AS TableName, MAX(ModDate) AS ModDate
FROM Tabl1 WHERE ModDate IS NOT NULL
UNION
SELECT 'Table2', MAX(ModDate) AS ModDate
FROM Table2 WHERE ModDate IS NOT NULL
END
END
GO

Exactly the same for a stored proc.
I'd also do this too because the code above won't work. CREATE xxxx must usually first in the batch. And your code will also find databases called "ContentFoo"
IF OBJECT_ID('dbo.myView') IS NOT NULL AND DB_NAME() = 'Content'
DROP VIEW [dbo].[ContentModDate]
GO
IF DB_NAME() = 'Content'
EXEC ('
CREATE VIEW [dbo].[Rx_ContentModDate] AS
SELECT ''Table1'' AS TableName, MAX(ModDate) AS ModDate
FROM Table1 WHERE ModDate IS NOT NULL
UNION
SELECT ''Table2'', MAX(ModDate) AS ModDate
FROM Table2 WHERE ModDate IS NOT NULL
')
Note: is the view name meant to be different?

Related

SQL script to select information from the same table on each databases?

I am trying to figure out how to write a SQL script to get information from Table1 on over 10 databases, how would I go about doing that?
In other words, I have over 10 databases with the exact same table called Table1, how can I write a script without repeating my code?
SELECT name, dbo.tDBVersion FROM MASTER.dbo.sysdatabases WHERE NAME LIKE 'AAA%'
I tried the above, but issue is this popup: "The multi-part identifier dbo.tDBVersion could not be bound "
My desired result would be 2 columns: name and dbversion number next to it.
EDIT: All my databases start with AAA
There is a stored procedure called sp_MSforeachdb. This will run the same command in each database - for example:
EXEC sp_MSforeachdb 'IF EXISTS(SELECT 1 FROM sys.tables WHERE name = ''tDBversion'') SELECT db_name(), col1, col2 FROM tDBversion'
https://www.mssqltips.com/sqlservertip/1414/run-same-command-on-all-sql-server-databases-without-cursors/
I went ahead and done this manually, then played about with temp tables and got what I wanted with the below:
USE AAA
SELECT DB_Name() 'Database', Version, CodeVersion INTO #TempTable FROM tDBVersion
USE AAB
INSERT INTO #TempTable
SELECT DB_Name() 'Database', Version, CodeVersion FROM tDBVersion
USE AAC
INSERT INTO #TempTable
SELECT DB_Name() 'Database', Version, CodeVersion FROM tDBVersion
SELECT * FROM #TempTable
-- ORDER BY "Version" asc
DROP TABLE #TempTable
-- My info sticks in the result pane while the table is dropped so that I don't forget to drop it

How to know which column is created in existing table?

I have around 500 tables in my database and each table is having a minimum of 100 columns.We total 5 person are working in the same database.So whenever requirement arises then a new column or a table is added.What ever I make changes,I keep a record but my colleagues didnt do it.So I am facing problem now what others have created column in the existing table or a new table is created.
So can anybody please tell me is it possible to know whether a new column is added to an existing table and if added what is the column name?
May be this query help you
SELECT
t.name AS table_name,
SCHEMA_NAME(schema_id) AS schema_name,
c.name AS column_name,
modify_date, create_date
FROM
sys.tables AS t
INNER JOIN
sys.columns c ON t.OBJECT_ID = c.OBJECT_ID
ORDER BY
modify_date DESC
EDIT
To Audit this, You have to use DDL trigger
Step 1:- Create New Audit Table
CREATE TABLE DDLAudit
(
PostTime datetime, DatabaseName varchar(256), Event nvarchar(100),
ObjectName varchar(256), TSQL nvarchar(2000), Login varchar(256)
)
Step 2:- Create DDL Trigger
CREATE TRIGGER AuditChanges
ON DATABASE
FOR CREATE_TABLE, ALTER_TABLE, DROP_TABLE
AS
DECLARE #ed XML
SET #ed = EVENTDATA()
INSERT INTO DDLAudit (PostTime, DatabaseName, Event, ObjectName, TSQL, Login)
VALUES
(
GetDate(),
#ed.value('(/EVENT_INSTANCE/DatabaseName)[1]', 'varchar(256)'),
#ed.value('(/EVENT_INSTANCE/EventType)[1]', 'nvarchar(100)'),
#ed.value('(/EVENT_INSTANCE/ObjectName)[1]', 'varchar(256)'),
#ed.value('(/EVENT_INSTANCE/TSQLCommand)[1]', 'nvarchar(2000)'),
#ed.value('(/EVENT_INSTANCE/LoginName)[1]', 'varchar(256)')
)
Now, Every Changes will be logged in Your DDLAudit. You can Filter out based on datetime filter on PostTime column.
Using the below query you can find the tables which were altered recently.
Query to know the table last altered
SELECT * FROM sys.tables
order by modify_date desc
Query to know the Column altered
SELECT TOP (select count(distinct(TransactionID))
from ::fn_trace_gettable( LEFT((select path from sys.traces where is_default = 1 ),len((select path from sys.traces where is_default = 1 )) - PATINDEX('%\%', reverse((select path from sys.traces where is_default = 1 )))) + '\log.trc', default )
where EventClass in (46,47,164) and EventSubclass = 0 and
DatabaseID <> 2 and
ObjectName='table1' and StartTime>'2015-01-10 00:00:00') [name],[colorder]
FROM [sys].[syscolumns]
where id=(SELECT object_id FROM sys.tables
where name='table1')
order by colorder desc
Note: this query will not work if there was any column dropped or the multiple columns of the table was altered using the SQL server UI but will keep track of multiple alter in the same query
The dropped column can be identified by the colorder. You will find the order will be missing but the column information you will not be able to see.
If you provide the table name and the date time, it gives the columns which were altered with order.
If it doesnt return any value then it means there was no change made on the table.

Finding #temp table in sysobjects / INFORMATION_SCHEMA

I am running a SELECT INTO statement like this so I can manipulate the data before finally dropping the table.
SELECT colA, colB, colC INTO #preop FROM tblRANDOM
However when I run the statement and then, without dropping the newly created table, I then run either of the following statements, the table isn't found? Even scanning through object explorer I can't see it. Where should I be looking?
SELECT [name] FROM sysobjects WHERE [name] = N'#preop'
SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = '#preop'
Temp tables aren't stored in the local database, they're stored in tempdb. Also their name isn't what you named them; it has a hex code suffix and a bunch of underscores to disambiguate between sessions. And you should use sys.objects or sys.tables, not the deprecated sysobjects (note the big warning at the top) or the incomplete and stale INFORMATION_SCHEMA views.
SELECT name FROM tempdb.sys.objects WHERE name LIKE N'#preop[_]%';
If you are trying to determine if such an object exists in your session, so that you know if you should drop it first, you should do:
IF OBJECT_ID('tempdb.dbo.#preop') IS NOT NULL
BEGIN
DROP TABLE #preop;
END
In modern versions (SQL Server 2016+), this is even easier:
DROP TABLE IF EXISTS #preop;
However if this code is in a stored procedure then there really isn't any need to do that... the table should be dropped automatically when the stored procedure goes out of scope.
I'd prefer to query tempdb in such manner:
IF EXISTS (SELECT * FROM tempdb.sys.objects
WHERE object_id = OBJECT_ID(N'tempdb.[dbo].[#MyProcedure]')
AND type in (N'P', N'PC'))
BEGIN
print 'dropping [dbo].[#MyProcedure]'
DROP PROCEDURE [dbo].[#MyProcedure]
END
GO
Below is how I got the columns for a temporary table:
CREATE TABLE #T (PK INT IDENTITY(1,1), APP_KEY INT PRIMARY KEY)
SELECT * FROM tempdb.INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME LIKE '#T%'

Can't assign value to column that was just created in table

For some reason when I run this, it says Invalid column name 'col3'.:
IF NOT EXISTS (SELECT name FROM SYS.COLUMNS WHERE name = 'col3' AND object_id IN (SELECT object_id
FROM SYS.TABLES WHERE name = 'table1')) BEGIN
ALTER TABLE table1 ADD col3 int
UPDATE table1 SET col3=col1+col2
END
But if I alter the table first and after the END of the IF I try to update the value of col3 like this, it just works:
IF NOT EXISTS (SELECT name FROM SYS.COLUMNS WHERE name = 'col3' AND object_id IN (SELECT object_id
FROM SYS.TABLES WHERE name = 'table1')) BEGIN
ALTER TABLE table1 ADD col3 int
END
UPDATE table1 SET col3=col1+col2
Why can't I update it when I create it?
This is a parse-time error - SQL Server is trying to validate the entire batch as a single, atomic operation. It doesn't see that you are going to add a column with that name, it just knows that there isn't a column with that name now - it evaluates this independently from all of the other statements in the batch. For the same reason you can't do this:
IF 1 = 1
CREATE TABLE #t(i INT);
ELSE
CREATE TABLE #t(y INT);
Obviously you and I know that only one of those branches will ever execute, but the error message you get from SQL Server (there is already an object named #t) hints that it doesn't understand branching or sequencing.
Two ways to circumvent this:
Issue the two commands in separate batches. If you are using Management Studio, simply put a GO between the ALTER and the UPDATE. This will force Management Studio to evaluate the batches in dependency order. Or even more simply - highlight the ALTER, and run that, then highlight the UPDATE, and run that.
Execute the update using dynamic SQL.
IF NOT EXISTS (blah blah)
BEGIN
ALTER TABLE dbo.table1 ADD col3 INT;
END
EXEC dbo.sp_executesql N'UPDATE dbo.table1 SET col3 = col1 + col2;';

How to create a "materialized something" that accesses different tables, depending on a specific setting

I want a program to access a table/view/stored procedure, etc. (something materialized, let's call it X) that abstracts the real location of the data contained in three basic tables (the tables have the same definition in all locations).
I would want X to fetch the server name, catalog name and table name from somewhere (a table, probably) and access the specific three basic tables. The caller of X would not know which specific tables were being called.
How can I do this in SQL Server (2008)?
Like a function, a view can't use dynamic SQL - it can't go find some metadata reference somewhere and adjust accordingly.
I think the closest thing to what you want is a synonym. Let's say you have three different databases, A, B and C. In A the table you want the view to reference is dbo.foo, in B it is dbo.bar, and in Cit is dbo.splunge. So then you could create a synonym like so in each database:
USE A;
GO
CREATE SYNONYM dbo.YourCommonViewName FOR dbo.foo;
GO
USE B;
GO
CREATE SYNONYM dbo.YourCommonViewName FOR dbo.bar;
GO
USE C;
GO
CREATE SYNONYM dbo.YourCommonViewName FOR dbo.splunge;
GO
Now this technically isn't a view, but in each database you can say...
SELECT <cols> FROM dbo.YourCommonViewName;
...and it will return the data from the database-specific table.
To do this in a stored procedure would be much simpler. Say you store the server, database and table name in some table, e.g. dbo.lookup:
CREATE TABLE dbo.lookup
(
id INT PRIMARY KEY,
[server] SYSNAME,
[database] SYSNAME,
[table] SYSNAME,
active BIT NOT NULL DEFAULT (0)
);
-- you may want a constraint or trigger to ensure
-- only one row can be active at any one time.
INSERT dbo.lookup(id, [server], [database], [table])
SELECT 1,N'serverA',N'databaseA',N'tableA'
UNION ALL SELECT 2,N'serverB',N'databaseB',N'tableB';
Now your program can say:
UPDATE dbo.lookup SET active = 1 WHERE ... ?
And your stored procedure can be:
CREATE PROCEDURE dbo.whatever
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX);
SELECT #sql = N'SELECT <cols> FROM ' + QUOTENAME([server])
+ '.' + QUOTENAME([database]) + '.dbo.' + QUOTENAME([table])
FROM dbo.lookup WHERE active = 1;
EXEC sp_executesql #sql;
END
GO
I still don't understand the point, and I don't know what you're planning to do when two different users expect to call your program at the same time, and they each should get results from a different location.
Agreed with Aaron on the fact that views and functions cannot use dynamic sql.
Still what you can do is build a clr table valued function. In that you can play with .net code and query whatever you want. And build you data accordingly and output what you need.
So instead of querying the data like
select * from myview
you can query it
select * from dbo.clr_mymockupview()
Create SYNONYMs to your remote servers.
Create your VIEW to concatenate your locations together using UNION ALL.
Since you said "tables", join your tables before the UNION ALL and hopefully, MS will perform the JOIN remotely.
Use a union query with parameters for database, server, and catalog:
Select col1, col2, <etc.>, 'table1' as tablename, 'server1' as servername, 'catalog1' as catname from server1.catalog1.table1
Union Select col1, col2, <etc.>, 'table2' as tablename, 'server2' as servername, 'catalog2' as catname from server2.catalog2.table2
Union Select col1, col2, <etc.>, 'table3' as tablename, 'server3' as servername, 'catalog3' as catname from server3.catalog3.table3
Then filter based on your 3 criteria. This probably won't be blazing fast but will wonk with std. SQL.