How to dynamiclly call a table from inside a view by name - sql

I need write a sql view on series of tables in a database. The problem is that the table only contains one month of history. Each month a new table is created. For example dbo.LOG_2015_09 would be September's table.
I need to write a view that shows me the last 60 days of history.
SELECT * FROM dbo.LOG_2015_09
UNION ALL
SELECT * FROM dbo.LOG_2015_08
The problem is that next month this will not be valid anymore.
I am limited to using SQL Views. Stored procedures are not an option.
One thought I had was to create a Table Function to get the relevant tables but I don't think we can use dynamic SQL to generate the code.
Thank you for any help.
EDIT: This is a sample of the table definition created by the application. I do not have any access to modify the table. I can only create views or functions.:
CREATE TABLE [dbo].[CLOG201509](
[LASTUPD] [datetime] NULL,
[CREDATE] [datetime] NULL,
[SERIALNO] [int] NOT NULL,
[LSEQNO] [int] NOT NULL,
[EVENTNO] [int] NOT NULL,
[EVDATE] [datetime] NOT NULL,
[LOGDATE] [datetime] NOT NULL,
CONSTRAINT [PK__CLOG2015__D08461DA672EF3E9] PRIMARY KEY CLUSTERED
([SERIALNO] ASC,
[EVENTNO] ASC,
[LSEQNO] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
These tables contain over a million records each.
EDIT: I have attempted to create a TABLE UDF to combine the tables but without being able to use Dynamic SQL I don't know how to make it change the tables based on the date. I would want to have this months table and last months table joined together to reference. The problem is that I have to go and remember to update this UDF each month and until I do, the data is unavailable.
CREATE FUNCTION [dbo].[KS_ManitouSync_OPT_CLOG]()
RETURNS TABLE
AS
RETURN
(
SELECT *
FROM CLOG201511
UNION ALL
SELECT *
FROM CLOG201512
)
As a reminder I cannot use Stored Procs, It must be useable by a program that supports a single SQL SELECT Query only.
Thanks for any help.

You could create a partitioned view which unions all the tables, but each table must have a constraint to limit the time interval allowed in it. In this case, when you select from the partitioned view, using a WHERE clause on the partitioning column, only the relevant tables will be accessed.
See https://technet.microsoft.com/en-us/library/ms190019(v=sql.105).aspx

create your view via a stored procedure. there you can figure out which log tables are all existing and then build your create view command dynamicaly.
have a look in a quite similar question i had in the past:
Creating View with dynamic columns by stored procedure

If really must use a view (you cannot use a stored procedure) and you cannot update the view automatically (using an SQL Server Agent job or using a DDL trigger), you could use dynamic SQL in a view using OPENROWSET, but there are a lot of caveats, see http://www.sommarskog.se/share_data.html#OPENQUERY

Related

Query is not using view index instead it is using normal table Primary Key index

I am developing a Power BI Report using direct query i.e every time my report is opened the query is being executed on my database and the returned rows will be displayed on the report.
I am using Azure SQL Database as the data source.
Below is my table structure from which I am fetching the data:
CREATE TABLE [reporting].[FactPaymentDetailsTable](
[CheckPayId] [int] NOT NULL,
[PaymentID] [int] NOT NULL,
[Department] [nvarchar](50) NULL,
[PaymentName] [nvarchar](50) NULL,
[TipAmt] [decimal](15, 4) NOT NULL,
[PayAmt] [decimal](15, 4) NOT NULL,
[BusinessDate] [date] NULL,
[Rounding] [decimal](21, 5) NOT NULL,
CONSTRAINT [PK_FactPaymentDetailsTable] PRIMARY KEY CLUSTERED
(
[CheckPayId] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
GO
Now my report is sending the below query to display a visual:
SELECT SUM([t0].[Rounding])
AS [a0]
FROM
(
(Select * From reporting.FactPaymentDetailsTable)
)
AS [t0]
This query is taking a bit longer as I am having a large amount of data in my table.
To optimize its performance I have created a view defined below:
create view [reporting].[TotalRoundingAmountView] with schemabinding
as
select SUM(rounding) as Rounding, COUNT_BIG(*) as CountBig
from reporting.FactPaymentDetailsTable
And created an index on the above view defined below:
CREATE UNIQUE CLUSTERED INDEX [ix_RoundingTotal] ON reporting.[TotalRoundingAmountView]
(
Rounding
)
So now when I execute the query i.e being executed by my report i.e the below query the expected result is that the query should use the view index. However, when I am looking into the query execution plan it is showing that the table is still using the table Primary Key index.
SELECT SUM([t0].[Rounding])
AS [a0]
FROM
(
(Select * From reporting.FactPaymentDetailsTable)
)
AS [t0]
Can anyone please help me out in this.
The exact same approach I have followed for my sales data and in that scenario, the query was using view index. I am not able to figure out why my query is not using view index in this scenario.
Any help is highly appreciated.
Thanks a lot in advance.
In this case the execution plan may consider it's faster to use table's index instead of view's, have a try of this keyword: "WITH (NOEXPAND)" to force the execution plan to use view's index.
If more detailed info needed, this article may be helpful
You may want to try adding a columnstore index instead of an indexed view. Indexed views are great for making specific queries go faster but they can have large impact on locking and concurrency (specifically in your case) as every update to the table will need to update that view and specifically the same row on that view. Columnstores and batch mode processing should give you a second path that has a more optimized execution engine for large data set queries where the storage structure is compressed and some answers are preaggregated for you. Please give it a try and see if it works better for you.

Creating a partitioned view in SQL Server 2008 R2 Enterprise

I'm extending some legacy software that splits data up in to multiple schemas by company, for example CP1.ACCOUNTS, CP2.ACCOUNTS, CPN.ACCOUNTS. I'm attempting to create an updatable view of these tables using partitioning, but I'm getting the typical "not updatable because a partitioning column was not found" error. The column I'm trying to partition on is the primary key, and as far as I can tell, isn't any of the things it isn't allowed to be.
So, with table definitions like so:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [CP1].[ACCOUNTS](
[ACCOUNTID] [char](10) NOT NULL,
[LASTNAME] [varchar](60) NOT NULL,
[FIRSTNAME] [varchar](35) NOT NULL,
[MIDDLE] [varchar](26) NULL,
[SUFFIX] [varchar](10) NULL,
[ADDRESS1] [varchar](55) NULL,
[ADDRESS2] [varchar](55) NULL,
[SOME_FLAG] [tinyint] NULL,
CONSTRAINT [ARM_CODE_KEY] PRIMARY KEY CLUSTERED
(
[CODE_] ASC
) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
ALTER TABLE [CP1].[ACCOUNTS] WITH CHECK ADD CONSTRAINT [CK__ACCOUNTS__CODE___4DD705FF] CHECK ((left([ACCOUNTID],(3))='CP1'))
GO
ALTER TABLE [CP1].[ACCOUNTS] CHECK CONSTRAINT [CK__ACCOUNTS__CODE___4DD705FF]
GO
ALTER TABLE [CP1].[ACCOUNTS] ADD DEFAULT ((0)) FOR [SOME_FLAG]
GO
and the rest of the tables defined exactly as above, following the CP2, CP3, CPN pattern, and the view definition being a simple:
CREATE VIEW [ALL].[ACCOUNTS] AS
SELECT * FROM CP1.ACCOUNTS
UNION ALL
SELECT * FROM CP2.ACCOUNTS
--UNION ALL etc...
Inserts would be like:
INSERT INTO [ALL].[ACCOUNTS]
([ACCOUNTID]
,[LASTNAME]
,[FIRSTNAME]
,[MIDDLE]
,[SUFFIX]
,[ADDRESS1]
,[ADDRESS2]
,[SOME_FLAG])
VALUES
('CP1XYZ0001',
'SMITH',
'JOHN',
'Q',
'',
'123 Fake St',
'Apt 2',
0,
GO
generates an error like:
Msg 4436, level 16, State 12, Line 1
UNION ALL view 'ALL.ACCOUNTS' is not updatable because a partitioning column was not found.
Am I missing something simple? Am I just way out in left field here?
You need a constraint that defines which column is used as a partitioning column. As the error suggests, you don't have one defined. As described in the documentation:
To perform updates on a partitioned view, the partitioning column must
be a part of the primary key of the base table. If a view is not
updatable, you can create an INSTEAD OF trigger on the view that
allows updates. You should design error handling into the trigger to
make sure that no duplicate rows are inserted. For an example of an
INSTEAD OF trigger designed on a view, see Designing INSTEAD OF
Triggers.
In other words, SQL Server needs to be able to figure out which table gets the update.
You might be able to alter the tables to contain a company name column, which is then used as part of the primary key. Something like this might work:
create table . . .
CompanyName as 'CompanyA',
primary key (AccountId, CompanyName)
. . .
The alternative is to use an instead of trigger, as suggested in the documentation.
In case someone comes upon this, you can use a computed column for partitioning, just make sure to make it a persisted computed column.
In this case, the computed column should be left([ACCOUNTID],(3) and the partition constraint would be <computed column> = 'CP1'. Note: using left() in the constraint will cause it to still scan all partitions. The CHECK constraints can only use these operators: BETWEEN, AND, OR, <, <=, >, >=, =.
Also, since the question referenced enterprise edition, you'd get better performance using a partitioned table instead of a partitioned view.

Access DateTime of row written to table if no DateTime field in table - SQL Server 2008

I have a situation where I need to find out the most recent entry written to a table but the person who developed the database created the table without a DateTime field or an auto-incrementing ID field.
I am wondering is there any way of accessing some built in DateTime proprty recorded by SQL Server or any other way of determining the more recent entry out of say two results like this:
idp_fund_id IDPID
14 1653
18 1653
Below is the structure
CREATE TABLE [dbo].[web_IDP_Lifestyle](
[idp_fund_id] [int] NOT NULL,
[IDPID] [int] NOT NULL,
CONSTRAINT [PK_web_IDP_Lifestyle] PRIMARY KEY CLUSTERED
(
[idp_fund_id] ASC,
[IDPID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
In short: no.
You might be able to compare for existence of those values in backups... depending on how far the backups go.
I'm assuming that the idp_fund_id of 18 might have been written before the value of 14?
Nope.
SQL Server doesn't keep detailed records on this. If it DID, think about how large the metadata would have to be in order to track the update/insert time for each and every record in each and every table.
SQL doesn't order inserts by default, and I don't think you can check what page it was inserted on to determine this with any reliability, either.
If you want to track something like this, you need to create a field/table for it to track it yourself and implement some triggers.

Script table as CREATE includes recent column additions as ALTERs?

I'm running SQL Server Management Studio 2008 against a SQL Server 2005 back-end. SSMS just exhibited a behavior I have never seen before. I don't know if this is something new in SSMS 2008 or just a function of something else.
Basically, what happened in that I added some new columns to an existing table. After adding those columns, I executed "Script table as...CREATE" within the IDE on the table. I expected to just get a single CREATE TABLE statement with all the rows, prvious and new. However, the generated code was the CREATE statement for the original definition of the table, plus individual ALTER TABLE T ADD [Column]... statements for each of the new columns.
This isn't a problem (and actually could be useful from a recent change management point-of-view ... sorta), but it is behavior I've never seen before.
I thought that this may have to do with row length, but the length comes in under the 8,000 byte limit before the table page gets split (forgive my terminology ... I'm a developer and not a DBA). Granted, it's not a small table (127 columns now with the additions and a little over 7,000 byte rowlength).
What am I seeing? Is this a feature/function of SSMS or SQL Server itself? Is this a side effect of the large table definition?
The following sample does not repeat the behavior, but it illustrates (simplified) what I'm seeing:
CREATE TABLE dbo.Table_1
(
ID int NOT NULL,
title nvarchar(50) NOT NULL
)
Then,
ALTER TABLE dbo.Table_1 ADD
[description] [varchar](50) NULL,
[numthing] [nchar](10) NULL
I expected to have this generated:
CREATE TABLE [dbo].[Table_1](
[ID] [int] NOT NULL,
[title] [nvarchar](50) NOT NULL,
[description] [varchar](50) NULL,
[numthing] [nchar](10) NULL,
However, this was generated:
CREATE TABLE [dbo].[Table_1](
[ID] [int] NOT NULL,
[title] [nvarchar](50) NOT NULL)
ALTER TABLE [dbo].[Table_1] ADD [description] [varchar](50) NULL
ALTER TABLE [dbo].[Table_1] ADD [numthing] [nchar](10) NULL
I suspect that you "cleaned up" the SQL in your post since it would normally contain many of other SET and GO statements. I am assuming that you removed the "SET ANSI_PADDING" statements.
Some columns in the table may have ANSI_PADDING set to ON while others are OFF. The ANSI_PADDING option affects all columns created after it was set Since the columns are going to be created in table order, the the ANSI_PADDING option will need to be used a few times depending on the table. The real problem is that MS SQL Server cannot set the ANSI_PADDING option within the CREATE TABLE statement. So it needs to do some of that work after the initial CREATE by using ALTER TABLE statements after the appropriate SET ANSI_PADDING statement.
See: http://kevine323.blogspot.com/2011/03/ansipadding-and-scripting-tables-from.html
I believe you're seeing Microsoft re-using two sections of SQL generation code.
I'm guessing you haven't saved the changes when you click to generate the CREATE script; so the generation code doesn't have an up-to-date version of the table to generate it from. Instead, it runs the normal generation code on the old table, and then the code behind the "Script changes" option to bring it up to date.
Clever code reuse with peculiar results.

Increasing performance on a logging table in SQL Server 2005

I have a "history" table where I log each request into a Web Handler on our web site. Here is the table definition:
/****** Object: Table [dbo].[HistoryRequest] Script Date: 10/09/2009 17:18:02 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[HistoryRequest](
[HistoryRequestID] [uniqueidentifier] NOT NULL,
[CampaignID] [int] NOT NULL,
[UrlReferrer] [nvarchar](512) NOT NULL,
[UserAgent] [nvarchar](512) NOT NULL,
[UserHostAddress] [nvarchar](15) NOT NULL,
[UserHostName] [nvarchar](512) NOT NULL,
[HttpBrowserCapabilities] [xml] NOT NULL,
[Created] [datetime] NOT NULL,
[CreatedBy] [nvarchar](100) NOT NULL,
[Updated] [datetime] NULL,
[UpdatedBy] [nvarchar](100) NULL,
CONSTRAINT [PK_HistoryRequest] PRIMARY KEY CLUSTERED
(
[HistoryRequestID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[HistoryRequest] WITH CHECK ADD CONSTRAINT [FK_HistoryRequest_Campaign] FOREIGN KEY([CampaignID])
REFERENCES [dbo].[Campaign] ([CampaignId])
GO
ALTER TABLE [dbo].[HistoryRequest] CHECK CONSTRAINT [FK_HistoryRequest_Campaign]
GO
37 seconds for 1050 rows on this statement:
SELECT *
FROM HistoryRequest AS hr
WHERE Created > '10/9/2009'
ORDER BY Created DESC
Does anyone have anysuggestions for speeding this up? I have a Clustered Index on the PK and a regular Index on the CREATED column. I tried a Unique Index and it barfed complaining there is a duplicate entry somewhere - which can be expected.
Any insights are welcome!
You are requesting all columns (*) over a non-covering index (created). On a large data set you are guaranteed to hit the Index Tipping Point where the clustered index scan is more efficient than an nonclustered index range seek and bookmark lookup.
Do you need * always? If yes, and if the typical access pattern is like this, then you must organize the table accordingly and make Created the leftmost clustered key.
If not, then consider changing your query to a coverable query, eg. select only HistoryRequestID and Created, which are covered by the non clustered index. If more fields are needed, add them as included columns to the non-clustered index, but take into account that this will add extra strorage space and IO log write time.
Hey, I've seen some odd behavior when pulling XML columns in large sets. Try putting your index on Created back, then specify the columns in your select statement; but omit the XML. See how that affects the return time for results.
For a log table, you probably don't need a uniqueidentifier column. You're not likely to query on it either, so it's not a good candidate for a clustered index. Your sample query is on "Created", yet there's no index on it. If you query frequently on ranges of "Created" values then it would be a good candidate for clustering even though it's not necessarily unique.
OTOH, the foreign key suggests frequent querying by Campaign, in which case having the clustering done by that column could make sense, and would also probably do a better job of scattering the inserted keys in the indexes - both the surrogate key and the timestamp would add records in sequential order, which is net more work over time for insertions because the node sectors are filled less randomly.
If it's just a log table, why does it have update audit columns? It would normally be write-only.
Rebuild indexes. Use WITH (NOLOCK) clause after the table names where appropriate, this probably applies if you want to run long(ish) running queries against table that are heavily used in a live environment (such as a log file). It basically means your query migth miss some of teh very latest records but you also aren't holding a lock open on the table - which creates additional overhead.