SQL Scripting Creating a view instead of derived table - sql-server

So This is a school question similar to another I was given a code:
USE AP
SELECT VendorName, FirstInvoiceDate, InvoiceTotal
FROM Invoices JOIN
(SELECT VendorID, MIN(InvoiceDate) AS FirstInvoiceDate
FROM Invoices
GROUP BY VendorID) AS FirstInvoice
ON (Invoices.VendorID = FirstInvoice.VendorID AND
Invoices.InvoiceDate = FirstInvoice.FirstInvoiceDate)
JOIN Vendors
ON Invoices.VendorID = Vendors.VendorID
ORDER BY VendorName, FirstInvoiceDate
I need to change this to create a view instead of a derived table I was thinking something more like
IF NOT EXISTS (SELECT * FROM sys.views
Where name = ‘EarliestInvoiceandTotalVEW’)
CREATE View AS
Select VendorName, FirstInvoiceDate, InvoiceTotal
From Invoices JOIN
(Create VIEW FirstInvoice AS
SELECT VendorID, MIN(InvoiceDate) AS FirstInvoiceDate
From Invoices
Group By VendorID) As FirstInvoice
ON (Invoices.VendorID = FirstInvoice.VendorID AND
Invoices.InvoiceDate = FirstInvoice.FirstInvoiceDate)
JOIN Vendors
ON Invoices.VendorID = Vendors.VendorID
ORDER BY VendorName, FirstInvoiceDate
This would make a view to do the same thing and check if it exists so it is not recreated/defined each time.
Thanks for any input on this!
I apologize about the format I took it directly out of SQL Server and it normally is formatted well...
This is what I got to work:
USE AP
Declare #Create1 varchar(8000)
IF EXISTS (SELECT * FROM sys.views Where sys.views.name = 'EarliestInvoiceandTotalVIEW')
drop view EarliestInvoiceandTotalVIEW;
SET #CREATE1 = 'CREATE View EarliestInvoiceTotalVIEW AS
Select VendorName, FirstInvoiceDate, InvoiceTotal
From Invoices JOIN
(SELECT VendorID, MIN(InvoiceDate) AS FirstInvoiceDate
FROM Invoices
GROUP BY VendorID) AS FirstInvoice
ON (Invoices.VendorID = FirstInvoice.VendorID AND
Invoices.InvoiceDate = FirstInvoice.FirstInvoiceDate)
JOIN Vendors
ON Invoices.VendorID = Vendors.VendorID'
Exec (#CREATE1)
I had to set the where to sys.views.name =... as well as set an exec command so that the create view is running 'first'.

In response to your comment, be sure to surround your view definition with GO statements:
if exists (select * from sys.views where name = 'ThreeDigitsOfPi')
drop view dbo.ThreeDigitsOfPi
GO
create view dbo.ThreeDigitsOfPi
as
select 3.14 as PI
GO
Some other points:
You should use single quotes ' instead of ‘ style quotes
Your view has to have a name (create view NameOfView as ...)
An view definition cannot follow an if statement, unless the view is created in dynamic SQL. For example: if not exists (...) exec('create view ...')

There are multiple ways to check if a view exists. However, often the approach is to delete a view if it exists and then recreate it:
IF EXISTS (SELECT * FROM sys.views Where name = ‘EarliestInvoiceandTotalVEW’)
drop view EarliestInvoiceandTotalVEW;
Then, your create view statement has two flaws. First it is lacking a name. Second it has an embedded create view in it. You have to decide which one you want to create -- a view for the subquery or a view for the entire statement (it is not clear to me from the question which is the right approach).

Related

auto pick schema change of underlying table for view

I know that the sql server doesnt pick the schema change of underlying table. for example, if we execute the following,
CREATE TABLE tbl (id INT)
CREATE VIEW viewa
AS
SELECT *
FROM tbl
ALTER TABLE tbl ADD NAME INT
and execute a select on view, only ID column is returned
SELECT *
FROM viewa
Is there any property I can set by which the sql engine autopicks the schema changes
use sp_refreshview for this,
exec sp_refreshview N'dbo.viewa'
There's no out of the box functionality for this. But you could roll your own. The system tables detail all the columns currently attached to each view and table.
-- Returns every column for the given table/view.
SELECT
c.*
FROM
sys.objects AS o
INNER JOIN sys.schemas AS s ON s.schema_id = o.schema_id
INNER JOIN sys.columns AS c ON c.object_id = o.object_id
WHERE
s.name = 'MySchema'
AND o.name = 'MyTable'
;
You would need to create a control table that maps your tables to views. You would also need to schedule the process.
You can create view with "SCHEMABINDING" option. That way base table definition cannot be modified in a way that could affect view definition.
CREATE VIEW viewa
AS
SELECT *
FROM tbl SCHEMABINDING

How to optimize view performance in SQL Server 2012 by indexing

I have a view like that:
create view dbo.VEmployeeSalesOrders
as
select
employees.employeeID, Products.productID,
Sum(Price * Quantity) as Total,
salesDate,
COUNT_BIG() as [RecordCount]
from
dbo.Employees
inner join
dbo.sales on employees.employeeID = sales.employeeID
inner join
dbo.products on sales.productID = products.ProductID
group by
Employees.employeeID, products.ProductID, salesDate
When I select * from dbo.VEmployeeSalesOrders it takes 97% of the execution plan. It needs it to be faster.
And when I try to create an index, an exception fires with the following message:
select list doesn't include a proper use on count_Big()
Why am getting this error?
1-first you need to alter your view and make it contains COUNT_BIG() function because you used aggregate function in select statment,
AND THE REASON FOR USING THAT is that SQL Server needs to track the record where the record is ,number of records
like this
create view dbo.VEmployeeSalesOrders
as
select employees.employeeID,Products.productID,Sum(Price*Quantity)
as Total,salesDate,COUNT_BIG(*) as [RecordCount]
from dbo.Employees
inner join dbo.sales on employees.employeeID=sales.employeeID
inner join dbo.products on sales.productID-products.ProductID
group by Employees.employeeID,products.ProductID,salesDate
2- then you need to create index like that
Create Unique Clustered Index Cidx_IndexName
on dbo.VEmployeeSalesOrders(employedID,ProductID,SalesDate)
Hope It Works

How can i join sys.columns of view to sys.columns of the table it is referencing in T-SQL?

I am trying to join records in sys.columns for a view, to the records in sys.columns for the table it is referencing, because i need the values of is_nullable, is_computed and default_object_id columns for the columns that are selected in the view.
The sys.columns records for the view have "incorrect" values, which you can observe by running the example queries below:
CREATE TABLE TestTable (
FieldA int NOT NULL,
FieldB int DEFAULT (1),
FieldC as CONVERT(INT, FieldA + FieldB),
FieldD int NOT NULL
)
GO
CREATE VIEW TestView WITH SCHEMABINDING AS
SELECT FieldA, FieldC as TestC, FieldB + FieldC as TestD
FROM dbo.TestTable WHERE FieldD = 1
GO
SELECT OBJECT_NAME(c.object_id) as ViewName, c.name as ColumnName,
c.is_nullable as Nullable, c.is_computed as Computed,
cast(CASE WHEN c.default_object_id > 0 THEN 1 ELSE 0 END as bit) as HasDefault
FROM sys.columns c
WHERE object_id = OBJECT_ID('TestTable')
GO
SELECT OBJECT_NAME(c.object_id) as ViewName, c.name as ColumnName,
c.is_nullable as Nullable, c.is_computed as Computed,
cast(CASE WHEN c.default_object_id > 0 THEN 1 ELSE 0 END as bit) as HasDefault
FROM sys.columns c
WHERE object_id = OBJECT_ID('TestView')
GO
I have tried using system views to join on dependencies, but they do not give us information about which column in the view refers to which column in the table:
-- dm_sql_referenced_entities gives us all columns referenced, but all records
-- have referencing_minor_id 0, so we do not know which column refers to what
SELECT * FROM sys.dm_sql_referenced_entities('dbo.TestView', 'OBJECT')
GO
-- sql_dependencies gives us all columns referenced, but all records has
-- column_id 0 so we can not use this either of joining the columns
SELECT * FROM sys.sql_dependencies WHERE object_id = OBJECT_ID('TestView')
GO
-- sql_expression_dependencies just tells us what table we are referencing
-- if view is not created WITH SCHEMABINDING. If it is, it will return columns,
-- but with referencing_minor_id 0 for all records, so not able use this either
SELECT * FROM sys.sql_expression_dependencies
WHERE referencing_id = OBJECT_ID('TestView')
GO
This unanswered post on social.msdn submitted by someone seems to be the same issue:
http://social.msdn.microsoft.com/Forums/en-US/transactsql/thread/4ae5869f-bf64-4eef-a952-9ac40c932cd4
You said "i need to know that TestView TestC refers to a computed column". This is not supported by SQL Server 2008 R2 (not sure for 2012 though, but i doubt it).
First you can query sys.columns or INFORMATION_SCHEMA.COLUMNS and you won't find what you want.
If you dig deeper, you will most probably try sys.sql_expression_dependencies and sys.dm_sql_referenced_entities (N'dbo.TestView', N'OBJECT'), but you can find table-column mapping there, not column-column. SQL server stores dependency information by 'high level' object (table, trigger...), not by its details (column). You will find same in sys.sysdepends. As a matter of fact dependency information is in SQL server unreliable.
At last, your only possibility would be to parse the view body by yourself. It can be found in sys.sql_modules:
SELECT m.definition
FROM
sys.objects o
JOIN sys.sql_modules m
ON m.object_id = o.object_id
WHERE
o.object_id = object_id('dbo.TestView')
and o.type = 'V'
Parsing T-SQL is VERY hard, it could really push you to the limit of your efforts. For instance, it should be more or less easy to grab table references from the view, and then table columns, especially if your view is schema-bound. But if it's not, well... just think of asterisks that reference OUTER APPLY, which references recursive CTE...
Anyway, good luck!
Currently, when views are created there are two operations that happen at a high level - parsing & binding. Parsing is basically checking for syntax of the statement, keywords & such. Binding is the process of mapping the identifiers (object names, column names) in the statement to the corresponding objects (tables, views, functions, columns etc.) & derivation of types. Additionally, in case of view similar to SELECT statements you can optionally alias column references and expressions in the SELECT list or after the view name (ex: create view v1(a) as select i from t).
After binding, we persist only the column aliases & the derived types in the metadata since a view is logically a table derived from a query expression. So there is currently no way to determine the expression that the column aliases map to or what it contains (columns or functions or literals etc.)
Only way to obtain the information you are looking for is to parse the view definition & perform your own binding. I believe we already have a bug that tracks the feature request to expose more richer dependency information regarding the mapping of aliases to column expressions in view definitions.
Lastly, SQL Server Developer Studio or Visual Studio Database Project uses the managed T-SQL parser to track such references so you can do refactoring or renaming for example using the project.
Hope this helps clarify the problem/current implementation.
It seems like the misunderstanding is the assumption that object_id is a primary key in sys.columns. It is not. The object_id in sys.columns relates to the object_id in sys.objects.
So:
SELECT C.*
FROM sys.objects T
INNER JOIN sys.columns C
ON T.object_id = C.object_id
WHERE T.type in ('S','U') -- System Tables and User Tables
AND T.name = 'Address' -- Table Name
order by C.Column_ID
will return the columns in the "Address" table in AdventureWorks.

How can I create a view from more than one table?

I have to create a view from more than one table in an MS SQL Server database, but I am not able to get the correct syntax for the same.
You'll have to provide more information about how you are looking to return data from more than one table. Typically you use JOINs:
CREATE VIEW your_view_vw AS
SELECT *
FROM TABLE_A a
JOIN TABLE_B b ON b.pk = a.fk
...where fk stands for "Foreign Key", and pk stands for "Primary Key" - assuming these constraints are in place. Maybe you need to use a Cross join instead? Here's a great visual representation of JOINs visually.
Reference:
CREATE VIEW
You do this with JOINs, just like you would with a regular query.
If you can write a query that gets you the data, you should be able to write view nearly the exact same way.
Post what you have.
example
create view ViewCustomerOrders
as
select * from Customer c
join Order o on o.CustomerID = c.CustomerID
create view viewname
as
select * from table a
join table b on b.col2 = a.col2
create view view_name
as
select * from table_A a
join table_B b on a.column_id = b.column_id

SQL Server (2005+) query to return the base table and base column (field) for each column (field) in a view

I want a query that will return a row for each column in a view, and a row for the view itself.
There should be a column basetable in the result that gives the base table for the column in the current row, and a column basefield in the result that gives the name of the column in the underlying query (for renamed columns). It would be a bonus if any calculations could also be included in the basefield column.
I don't think this can be done. Am I wrong?
In the example below "what goes here" should be replaced by table1 or table2 as appropriate in the basetable column, and a, b, or c as appropriate in the basefield column.
create table table1 (a int, b int)
create table table2 (a int, c int)
go
create view view1 as select table1.a, table1.b, table2.c from table1 left join table2 on table1.a = table2.a
go
select * from
(
select 'View' objecttype,O.name viewname,'' fieldname,0 column_id,'' typename,'' max_length,'' [precision], '' scale, '' is_identity,
'what goes here' basetable, '' basefield
from sys.objects O where O.type='V' and O.[schema_id] = 1
union all
select 'Field' objecttype,object_name(C.[object_id]) viewname,C.name fieldname,C.column_id,T.name typename,C.max_length,C.precision,C.scale,C.is_identity,
'what goes here' basetable, 'what goes here' basefield
from sys.columns C
left join sys.types T on C.user_type_id=T.system_type_id
where C.[object_id] in (select O.[object_id] from sys.objects O where O.type='V')
) I
where viewname in ('view1')
order by viewname, column_id
drop view view1
drop table table1
drop table table2
There are few tables in the information schema that you can use to deduce the information. A basic query that gives you quite a bit of data will be:
select *
from INFORMATION_SCHEMA.VIEW_COLUMN_USAGE v
inner join INFORMATION_SCHEMA.COLUMNS v1
on v.VIEW_NAME=v1.TABLE_NAME and v.COLUMN_NAME=v1.COLUMN_NAME
where v.VIEW_NAME='My_View'
The tables in the information schema are:
select * from INFORMATION_SCHEMA.VIEWS
select * from INFORMATION_SCHEMA.VIEW_TABLE_USAGE
select * from INFORMATION_SCHEMA.VIEW_COLUMN_USAGE
So try using these.
However it works only when you use columns directly from the base tabbles without any formula ar deriving.
You know it might not map 1 to 1? A column in a view might be the result of several (or even none!) columns from different tables.
That said, it should be possible by parsing the source for the view. But the sql code would be procedural/imperative in nature and not at all trivial.

Resources