CREATE VIEW in SQL Server using UNION ALL - sql-server

Please consider the below example:
CREATE VIEW VW_YearlySales
AS
SELECT 2011 AS YearNo, ProductID, SUM(Amount) FROM InvoiceTable2011
UNION ALL
SELECT 2012 AS YearNo, ProductID, SUM(Amount) FROM InvoiceTable2012
UNION ALL
SELECT 2013 AS YearNo, ProductID, SUM(Amount) FROM InvoiceTable2013
GO
The InvoiceTable2013 doesn't exist actually and I don't want to create it right now, it will be created automatically when recording the first invoice for year 2013.
Can anyone help me on how to specify a condition that will verify the existence of the table before doing the UNION ALL?
Many thanks for your help.

As others have correctly said, you can't achieve this with a view, because the select statement has to reference a concrete set of tables - and if any of them don't exist, the query will fail to execute.
It seems to me like your problem is more fundamental. Clearly there should conceptually be exactly one InvoiceTable, with rows for different dates. Separating this out into different logical tables by year is presumably something that's been done for optimisation (unless the columns are different, which I very much doubt).
In this case, partitioning seems like the way to remedy this problem (partitioning large tables by year/quarter/month is the canonical example). This would let you have a single InvoiceTable logically, yet specify that SQL Server should store the data behind the scenes as if it were different tables split out by year. You get the best of both worlds - an accurate model, and fast performance - and this makes your view definition simple.

No, according to my knowledge its not possible in view, you have to use Stored Procedure. In Stored Procedure you can validate table existance & based on the existance of that table you can change your SQL.
EDIT:
CREATE PROCEDURE GetYearlySales
AS
IF (EXISTS (SELECT *
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'InvoiceTable2013'))
BEGIN
SELECT 2011 AS YearNo, ProductID, SUM(Amount) FROM InvoiceTable2011
UNION ALL
SELECT 2012 AS YearNo, ProductID, SUM(Amount) FROM InvoiceTable2012
UNION ALL
SELECT 2013 AS YearNo, ProductID, SUM(Amount) FROM InvoiceTable2013
END
ELSE
BEGIN
SELECT 2011 AS YearNo, ProductID, SUM(Amount) FROM InvoiceTable2011
UNION ALL
SELECT 2012 AS YearNo, ProductID, SUM(Amount) FROM InvoiceTable2012
END

Looks like you want to have a table for every year and you want to ensure that you have the query for SP without modifying the SP. This is slightly risky , you will have to maintain the naming conventions all the time. In this case what you will have to do is query the informationschema tables for table_name like 'InvoiceTable%'. Get the records in a table and then loop through the records attaching the fixed SQL. And then execute the dynamic sql like its done here http://www.vishalseth.com/post/2008/07/10/Dynamic-SQL-sp_executesql.aspx

Related

SQL Server : group all data by one column

I need some help in writing a SQL Server stored procedure. All data group by Train_B_N.
my table data
Expected result :
expecting output
with CTE as
(
select Train_B_N, Duration,Date,Trainer,Train_code,Training_Program
from Train_M
group by Train_B_N
)
select
*
from Train_M as m
join CTE as c on c.Train_B_N = m.Train_B_N
whats wrong with my query?
The GROUP BY smashes the table together, so having columns that are not GROUPED combine would cause problems with the data.
select Train_B_N, Duration,Date,Trainer,Train_code,Training_Program
from Train_M
group by Train_B_N
By ANSI standard, the GROUP BY must include all columns that are in the SELECT statement which are not in an aggregate function. No exceptions.
WITH CTE AS (SELECT TRAIN_B_N, MAX(DATE) AS Last_Date
FROM TRAIN_M
GROUP BY TRAIN_B_N)
SELECT A.Train_B_N, Duration, Date,Trainer,Train_code,Training_Program
FROM TRAIN_M AS A
INNER JOIN CTE ON CTE.Train_B_N = A.Train_B_N
AND CTE.Last_Date = A.Date
This example would return the last training program, trainer, train_code used by that ID.
This is accomplished from MAX(DATE) aggregate function, which kept the greatest (latest) DATE in the table. And since the GROUP BY smashed the rows to their distinct groupings, the JOIN only returns a subset of the table's results.
Keep in mind that SQL will return #table_rows X #Matching_rows, and if your #Matching_rows cardinality is greater than one, you will get extra rows.
Look up GROUP BY - MSDN. I suggest you read everything outside the syntax examples initially and obsorb what the purpose of the clause is.
Also, next time, try googling your problem like this: 'GROUP BY, SQL' or insert the error code given by your IDE (SSMS or otherwise). You need to understand why things work...and SO is here to help, not be your google search engine. ;)
Hope you find this begins your interest in learning all about SQL. :D

Selecting count without Group By

I have following table in SQL Server 2005. One order can have multiple containers. A container can be either Plastic or wood (New types may come in future).
I need to list the following columns -
OrderID, ContainerType, ContainerCOUNT and ContainerID.
Since I need to list the ContainerID also, the following group by approach won’t work.
DECLARE #OrderCoarntainers TABLE (OrderID INT, ContainerID INT, ContainerType VARCHAR(10))
INSERT INTO #OrderCoarntainers VALUES (1,101,'Plastic')
INSERT INTO #OrderCoarntainers VALUES (1,102,'Wood')
INSERT INTO #OrderCoarntainers VALUES (1,103,'Wood')
INSERT INTO #OrderCoarntainers VALUES (2,104,'Plastic')
SELECT OrderID,ContainerType,COUNT(DISTINCT ContainerID) AS ContainerCOUNT
FROM #OrderCoarntainers
GROUP BY OrderID,ContainerType
What is the best way to achive this?
Note: Upgrading SQL Server version is not an option for me.
Expected Result
You should be able to use a windowed function
SELECT OrderID,
ContainerType,
COUNT(ContainerID) OVER (PARTITION BY OrderID, ContainerType) AS ContainerCOUNT,
ContainerID
FROM #OrderCoarntainers
I really don't know SQL Server dialect of SQL that well, but I can suggest something that is pretty basic and may work. It relies on a join, which is not optimal for performance but will get the job done if the table is not huge or performance is not critical. Really the problem here is the table design is pretty bad for the data you are managing, as this should not all be in one table. But anyway:
SELECT o1.OrderID, o1.ContainerType, count(o2.ContainerID) AS ContainerCOUNT, o1.ContainerID
FROM #OrderCoarntainers o1 JOIN #OrderCoarntainers o2
ON o1.OrderID = o2.orderID AND o1.ContainerType = o2.ContainerType
GROUP BY o1.OrderID

Paging in SQL Server problems

I have searched for paging in SQL Server. I found most of the solution look like that
What is the best way to paginate results in SQL Server
But it don't meet my expectation.
Here is my situation:
I work on JasperReport, for that: to export the report I just need pass the any Select query into the template, it will auto generated out the report
EX : I have a select query like this:
Select * from table A
I don't know any column names in table A. So I can't use
Select ROW_NUMBER() Over (Order By columsName)
And I also don't want it order by any columns.
Anyone can help me do it?
PS: In Oracle , it have rownum very helpful in this case.
Select * from tableA where rownum > 100 and rownum <200
Paging with Oracle
You should use ROW_NUMBER with an ORDER BY - because without an ORDER BY there is no determinism in how rows are returned. You can run the same query three times and get the results back in three different orders. Especially if merry-go-round scans come into play.
So unless you want your report to have the possibility of showing the same rows to users on multiple pages, or some rows never on any page, you need to find a way to order the result set to make it deterministic.
From my opinion, you can use sql query to find out how many columns in a table, and then find out a proper one for ' order by ' to depend on.
The script of how to get out columns of an table refer to : How can I get column names from a table in SQL Server?
Check out this link
http://msdn.microsoft.com/en-us/library/ms186734.aspx
SQL Server has similar function ROW_NUMBER. Though it behaves a bit differently.
SQL Server provides no guarantee of row order unless you have have specified a column in order by clause. I would recommend that you give an order by clause that has unique values.
Thank for all your help. Because of order by are required when paging in MS SQL Server, so I used ResultSetMetaData to get the Columns name and do paging as well.
You can use the below query aswell.
declare #test table(
id int,
value1 varchar(100),
value2 int)
insert into #test values(1,'10/50',50)
insert into #test values(2,'10/60',60)
insert into #test values(3,'10/60',61)
insert into #test values(4,'10/60',10)
insert into #test values(5,'10/60',11)
insert into #test values(6,'10/60',09)
select *
from ( select row_number() over (order by (select 0)) as rownumber,* from #test )test
where test.rownumber<=5

SQL Server Pagination w/o row_number() or nested subqueries?

I have been fighting with this all weekend and am out of ideas. In order to have pages in my search results on my website, I need to return a subset of rows from a SQL Server 2005 Express database (i.e. start at row 20 and give me the next 20 records). In MySQL you would use the "LIMIT" keyword to choose which row to start at and how many rows to return.
In SQL Server I found ROW_NUMBER()/OVER, but when I try to use it it says "Over not supported". I am thinking this is because I am using SQL Server 2005 Express (free version). Can anyone verify if this is true or if there is some other reason an OVER clause would not be supported?
Then I found the old school version similar to:
SELECT TOP X * FROM TABLE WHERE ID NOT IN (SELECT TOP Y ID FROM TABLE ORDER BY ID) ORDER BY ID where X=number per page and Y=which record to start on.
However, my queries are a lot more complex with many outer joins and sometimes ordering by something other than what is in the main table. For example, if someone chooses to order by how many videos a user has posted, the query might need to look like this:
SELECT TOP 50 iUserID, iVideoCount FROM MyTable LEFT OUTER JOIN (SELECT count(iVideoID) AS iVideoCount, iUserID FROM VideoTable GROUP BY iUserID) as TempVidTable ON MyTable.iUserID = TempVidTable.iUserID WHERE iUserID NOT IN (SELECT TOP 100 iUserID, iVideoCount FROM MyTable LEFT OUTER JOIN (SELECT count(iVideoID) AS iVideoCount, iUserID FROM VideoTable GROUP BY iUserID) as TempVidTable ON MyTable.iUserID = TempVidTable.iUserID ORDER BY iVideoCount) ORDER BY iVideoCount
The issue is in the subquery SELECT line: TOP 100 iUserID, iVideoCount
To use the "NOT IN" clause it seems I can only have 1 column in the subquery ("SELECT TOP 100 iUserID FROM ..."). But when I don't include iVideoCount in that subquery SELECT statement then the ORDER BY iVideoCount in the subquery doesn't order correctly so my subquery is ordered differently than my parent query, making this whole thing useless. There are about 5 more tables linked in with outer joins that can play a part in the ordering.
I am at a loss! The two above methods are the only two ways I can find to get SQL Server to return a subset of rows. I am about ready to return the whole result and loop through each record in PHP but only display the ones I want. That is such an inefficient way to things it is really my last resort.
Any ideas on how I can make SQL Server mimic MySQL's LIMIT clause in the above scenario?
Unfortunately, although SQL Server 2005 Row_Number() can be used for paging and with SQL Server 2012 data paging support is enhanced with Order By Offset and Fetch Next, in case you can not use any of these solutions you require to first
create a temp table with identity column.
then insert data into temp table with ORDER BY clause
Use the temp table Identity column value just like the ROW_NUMBER() value
I hope it helps,

Possible to test for null records in SQL only?

I am trying to help a co-worker with a peculiar problem, and she's limited to MS SQL QUERY code only. The object is to insert a dummy record (into a surrounding union) IF no records are returned from a query.
I am having a hard time going back and forth from PL/SQL to MS SQL, and I am appealing for help (I'm not particularly appealing, but I am appealing to the StackOverflow audiance).
Basically, we need a single, testable value from the target Select ... statement.
In theory, it would do this:
(other records from unions)
Union
Select "These" as fld1, "are" as fld2, "Dummy" as fld3, "Fields" as fld4
where NOT (Matching Logic)
Union
Select fld1, fld2, fld3, fld4 // Regular records exist
From tested_table
Where (Matching Logic)
Forcing an individual dummy record, with no conditions, works.
IS there a way to get a single, testable result from a Select?
Can't do it in code (not allowed), but can feed SQL
Anybody? Anybody? Bbeller?
You could put the unions in a with, then include another union that returns a null only when the big union is empty:
; with BigUnion as
(
select *
from table1
union all
select *
from table2
)
select *
from BigUnion
union all
select null
where not exists (select * from BigUnion)

Resources