Order BY is not supported in view in sql server - sql-server

i am trying to create a view in sql server.
create view distinct_product as
select distinct name from stg_user_dtlprod_allignmnt_vw order by name;
this is showing an error.
error message is:
The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP or FOR XML is also specified.
plz help me out where i am wrong.

You could use TOP with a number that is greater than the number of records:
CREATE VIEW [dbo].[distinct_product]
AS
SELECT DISTINCT TOP 10000000 name
FROM stg_user_dtlprod_allignmnt_vw
ORDER BY name
You cannot use TOP 100 PERCENT since the optimizer recognizes that TOP 100 PERCENT qualifies all rows and does not need to be computed at all, so the ORDER BY wouldn't be guaranteed.

A view cannot be sorted with an ORDER BY clause. You need to put the ORDER BY clause into any query that references the view.
A view is not materialized - the data isn't stored, so how could it be sorted? A view is kind of like a stored procedure that just contains a SELECT with no parameters... it doesn't hold data, it just holds the definition of the query. Since different references to the view could need data sorted in different ways, the way that you do this - just like selecting from a table, which is also an unsorted collection of rows, by definition - is to include the order by on the outer query.

You can't order a view like that when it's created as the message states, unless you follow the other answers from Tim / Raphael, but you can order results selected from a view:
So create it in step 1:
create view distinct_product as
select distinct name
from stg_user_dtlprod_allignmnt_vw
Then order it when you retrieve data:
select *
from distinct_product
order by name

Related

how to select first rows distinct by a column name in a sub-query in sql-server?

Actually I am building a Skype like tool wherein I have to show last 10 distinct users who have logged in my web application.
I have maintained a table in sql-server where there is one field called last_active_time. So, my requirement is to sort the table by last_active_time and show all the columns of last 10 distinct users.
There is another field called WWID which uniquely identifies a user.
I am able to find the distinct WWID but not able to select the all the columns of those rows.
I am using below query for finding the distinct wwid :
select distinct(wwid) from(select top 100 * from dbo.rvpvisitors where last_active_time!='' order by last_active_time DESC) as newView;
But how do I find those distinct rows. I want to show how much time they are away fromm web apps using the diff between curr time and last active time.
I am new to sql, may be the question is naive, but struggling to get it right.
If you are using proper data types for your columns you won't need a subquery to get that result, the following query should do the trick
SELECT TOP 10
[wwid]
,MAX([last_active_time]) AS [last_active_time]
FROM [dbo].[rvpvisitors]
WHERE
[last_active_time] != ''
GROUP BY
[wwid]
ORDER BY
[last_active_time] DESC
If the column [last_active_time] is of type varchar/nvarchar (which probably is the case since you check for empty strings in the WHERE statement) you might need to use CAST or CONVERT to treat it as an actual date, and be able to use function like MIN/MAX on it.
In general I would suggest you to use proper data types for your column, if you have dates or timestamps data use the "date" or "datetime2" data types
Edit:
The query aggregates the data based on the column [wwid], and for each returns the maximum [last_active_time].
The result is then sorted and filtered.
In order to add more columns "as-is" (without aggregating them) just add them in the SELECT and GROUP BY sections.
If you need more aggregated columns add them in the SELECT with the appropriate aggregation function (MIN/MAX/SUM/etc)
I suggest you have a look at GROUP BY on W3
To know more about the "execution order" of the instruction you can have a look here
You can solve problem like this by rank ordering the results by a key and finding the last x of those items, this removes duplicates while preserving the key order.
;
WITH RankOrdered AS
(
SELECT
*,
wwidRank = ROW_NUMBER() OVER (PARTITION BY wwid ORDER BY last_active_time DESC )
FROM
dbo.rvpvisitors
where
last_active_time!=''
)
SELECT TOP(10) * FROM RankOrdered WHERE wwidRank = 1
If my understanding is right, below query will give the desired output.
You can have conditions according to your need.
select top 10 distinct wwid from dbo.rvpvisitors order by last_active_time desc

Update row_number value in view joined to different table

I created a view in SQL Server that includes a row_number() function. The table referenced in the view contains every record in my database and enumerates records based on duplicate instances of a composite ID. For example:
row_number() over (
partition by
composite_id
order by
sample_value) as rownum
The issue is that whenever I join this view against another table (or filter rows based on a WHERE clause), the row number nevertheless always returns the value that would be returned for the full table referenced in the view. Instead, I'd like the row number to update depending on the records that are ultimately returned in the eventual result set.
For example:
select *
from my_created_view a
where a.sample_value in ('a','b','c')
or
select *
from my_created_view a
inner join subset_of_data b on a.sample_value = b.sample_value
...where either query above would result in a smaller number of records than are contained in the full original table and the resulting set of composite_id would sometimes contain only one instance. In cases where the result set contains only one instance of composite_id, I'd like that row to receive a value of 1.
Is this possible? Or does row numbering within a view create a row number that's tied only to the query within the created view?
Thanks in advance for any light you can shed here!

Hierarchical SQL select-query

I'm using MS SqlServer 2008. And I have a table 'Users'. This table has the key field ID of bigint. And also a field Parents of varchar which encodes all chain of user's parent IDs.
For example:
User table:
ID | Parents
1 | null
2 | ..
3 | ..
4 | 3,2,1
Here user 1 has no parents and user 4 has a chain of parents 3->2->1. I created a function which parses the user's Parents field and returns result table with user IDs of bigint.
Now I need a query which will select and join IDs of some requested users and theirs parents (order of users and theirs parents is not important). I'm not an SQL expert so all I could come up with is the following:
WITH CTE AS(
SELECT
ID,
Parents
FROM
[Users]
WHERE
(
[Users].Name = 'John'
)
UNION ALL
SELECT
[Users].Id,
[Users].Parents
FROM [Users], CTE
WHERE
(
[Users].ID in (SELECT * FROM GetUserParents(CTE.ID, CTE.Parents) )
))
SELECT * FROM CTE
And basically it works. But performance of this query is very poor. I believe WHERE .. IN .. expression here is a bottle neck. As I understand - instead of just joining the first subquery of CTE (ID's of found users) with results of GetUserParents (ID's of user parents) it has to enumerate all users in the Users table and check whether the each of them is a part of the function's result (and judging on execution plan - Sql Server does distinct order of the result to improve performance of WHERE .. IN .. statement - which is logical by itself but in general is not required for my goal. But this distinct order takes 70% of execution time of the query). So I wonder how this query could be improved or perhaps somebody could suggest some another approach to solve this problem at all?
Thanks for any help!
The recursive query in the question looks redundant since you already form the list of IDs needed in GetUserParents. Maybe change this into SELECT from Users and GetUserParents() with WHERE/JOIN.
select Users.*
from Users join
(select ParentId
from (SELECT * FROM Users where Users.Name='John') as U
cross apply [GetDocumentParents](U.ID, U.Family, U.Parents))
as gup
on Users.ID = gup.ParentId
Since GetDocumentParents expects scalars and select... where produces a table, we need to apply the function to each row of the table (even if we "know" there's only one). That's what apply does.
I used indents to emphasize the conceptual parts of the query. (select...) as gup is the entity Users is join'd with; (select...) as U cross apply fn() is the argument to FROM.
The key knowledge to understanding this query is to know how the cross apply works:
it's a part of a FROM clause (quite unexpectedly; so the syntax is at FROM (Transact-SQL))
it transforms the table expression left of it, and the result becomes the argument for the FROM (i emphasized this with indent)
The transformation is: for each row, it
runs a table expression right of it (in this case, a call of a table-valued function), using this row
adds to the result set the columns from the row, followed by the columns from the call. (In our case, the table returned from the function has a single column named ParentId)
So, if the call returns multiple rows, the added records will be the same row from the table appended with each row from the function.
This is a cross apply so rows will only be added if the function returns anything. If this was the other flavor, outer apply, a single row would be added anyway, followed by a NULL in the function's column if it returned nothing.
This "parsing" thing violates even the 1NF. Make Parents field contain only the immediate parent (preferably, a foreign key), then an entire subtree can be retrieved with a recursive query.

SQL Server Indexed View Error

I realize this is a very contrived example, but I've simplified the full version down to the following which demonstrates the problem:
CREATE VIEW model.Appointments_Partition1
WITH SCHEMABINDING AS
SELECT CONVERT(varchar(15), AppointmentId) as Id,
ap.AppTypeId as AppointmentTypeId,
ap.Duration as DurationMinutes,
ap.AppointmentId as EncounterId,
COUNT_BIG(*) as __count_big
FROM dbo.Appointments ap
JOIN dbo.PracticeCodeTable pct ON SUBSTRING(pct.Code, 1, 1) = ap.ScheduleStatus
AND pct.ReferenceType = 'AppointmentStatus'
WHERE ap.AppTime > 0
GROUP BY CONVERT(varchar(15), AppointmentId), ap.AppTypeId, ap.Duration, ap.AppointmentId
CREATE UNIQUE CLUSTERED INDEX [IX_Appointments_Partition1_Id]
ON model.Appointments_Partition1 ([Id]);
I get:
Msg 8668, Level 16, State 0, Line 12
Cannot create the clustered index 'IX_Appointments_Partition1_Id' on view 'PracticeRepository.model.Appointments_Partition1' because the select list of the view contains an expression on result of aggregate function or grouping column. Consider removing expression on result of aggregate function or grouping column from select list.
I'm including count_big...so why is the group by a problem?....and how can I resolve the error?
Here is the same error message with some boolean logic applied to it:
Cannot create the clustered index '...' on view '...' because the
select list of the view contains an expression on a grouping column.
Consider removing expression on a grouping column from the select list.
You need to remove the CONVERT in CONVERT(varchar(15), AppointmentId)
I find this reason on one of the blogs, seems reasonable to me
No, you can't use schema binding on a view that has an aggregate. And you can't index a view unless you use schema binding. You also can't bind an index that uses outer or left joins. Basically, you can only bind a view that contains a simple select statement.
http://www.tek-tips.com/viewthread.cfm?qid=1401646
You can go through the blog and see if it exactly matched your scenario.
http://technet.microsoft.com/en-us/library/cc917715.aspx
If you want to build index on views, then you must create views with schema binding, in the above link it is explained in detail. Go through the section of Design Considerations

Stored procedure?

I write a stored procedure for most viewed photos in my procedure, this is my procedure can u check this please is ok or is there any improvement required?
create procedure sp_photos_selectmostviewedphotos
as
select * from photos order by views desc
is it enough or required any modification
First just specify the columns you really need -> replace the star in your query.
Then create an index over the views column (SortOrder DESC).
The rest should be OK :)
+1 to Greco, just to add:
I'd imagine you won't actually use ALL the records (the name indicates "most viewed photos"), so I'd stick in a TOP clause and only return however many records you actually need.
e.g.
SELECT TOP 10 Column1, Column2
FROM Photos
ORDER BY Views DESC

Resources