views:

278

answers:

2

Hi ,

I have a stored Procedure which do Sorting and Paging like the Sorting Custom Paged Results of of Scott Michell . I have two tables: Article and Category ,My StoredProcedure works fine for Article Table , But i want add a field of Category into queyr (Inner Join I mean )
Actually I can't do like the Scott Michell has done , Because there are some field that are similar at both Tables .(When i do like Scott all the time i get "Ambiguous Column Error") My StoredProcedure without Inner join is :

set ANSI_NULLS ON
set QUOTED_IDENTIFIER ON
go


ALTER PROCEDURE [dbo].[sp_Articles_SelectByCategoryId]
 @CategoryId int,
 @startRowIndex int = -1,
 @maximumRows int = -1,
 @sortExpression nvarchar(50),
 @recordCount int = NULL OUTPUT

AS

IF (@recordCount IS NOT NULL)
 BEGIN
  SET @recordCount = (SELECT COUNT(*) FROM [dbo].[Articles] WHERE [CategoryId] = @CategoryId
 )
  RETURN
 END

IF LEN(@sortExpression) = 0
   SET @sortExpression = 'Id'

DECLARE @sql nvarchar(4000)
SET @sql = 
'SELECT
 [Id],
 [AddedDate],
 [AddedBy],
 [CategoryId],
 [Title],
 [Abstract],
 [Body]

FROM
 (SELECT 

 [Id],
 [AddedDate],
 [AddedBy],
 [CategoryId],
 [Title],
 [Abstract],
 [Body],

ROW_NUMBER() OVER(ORDER BY ' + @sortExpression + ') as RowNum

 FROM  [dbo].[Articles]

 WHERE CategoryId = ' + CONVERT(nvarchar(10), @CategoryId) + '
 ) as CategoryIdInfo
 WHERE 
 ((RowNum between (' + CONVERT(nvarchar(10), @startRowIndex) + ') AND ' + CONVERT(nvarchar(10), @startRowIndex) + ' + ' + CONVERT(nvarchar(10), @maximumRows) + ' - 1) 
   OR ' + CONVERT(nvarchar(10), @startRowIndex) + ' = -1 OR ' + CONVERT(nvarchar(10), @maximumRows) + ' = -1)'

  -- Execute the SQL query
  EXEC sp_executesql @sql


--endregion

My Category Table is :

CREATE TABLE [dbo].[Category](
 [Id] [int] IDENTITY(1,1) NOT NULL,
 [AddedDate] [datetime] NOT NULL,
 [AddedBy] [nvarchar](250) COLLATE Arabic_CI_AS NOT NULL,
 [Title] [nvarchar](50) COLLATE Arabic_CI_AS NOT NULL,
 [Importance] [int] NOT NULL,
 [Description] [nvarchar](300) COLLATE Arabic_CI_AS NULL,
 [ImageUrl] [nvarchar](50) COLLATE Arabic_CI_AS NULL,
 CONSTRAINT [PK_Category] PRIMARY KEY CLUSTERED 
(
 [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

My Article Table :

CREATE TABLE [dbo].[Articles](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [AddedDate] [datetime] NOT NULL,
    [AddedBy] [nvarchar](250) COLLATE Arabic_CI_AS NOT NULL,
    [CategoryId] [int] NOT NULL,
    [Title] [nvarchar](255) COLLATE Arabic_CI_AS NOT NULL,
    [Abstract] [nvarchar](4000) COLLATE Arabic_CI_AS NULL,
    [Body] [nvarchar](max) COLLATE Arabic_CI_AS NOT NULL,
    [ReleaseDate] [datetime] NULL,
    [ExpireDate] [datetime] NULL,
    [Approved] [bit] NOT NULL,
    [Listed] [bit] NOT NULL,
    [CommentEnabled] [bit] NOT NULL,
    [OnlyForMembers] [bit] NOT NULL,
    [ViewCount] [int] NOT NULL,
    [Votes] [int] NOT NULL,
    [TotalRating] [int] NOT NULL,
 CONSTRAINT [PK_Articles] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

I don't know how to add The "Title" Column of "Category" Table into Query .

if my Category table hadn't similar fields, surely this query workes :

DECLARE @sql nvarchar(4000)
SET @sql = 
'SELECT
    [Id],
    [AddedDate],
    [AddedBy],
    [CategoryId],
    [Title],
    [Abstract],
    [Body],
    ArticleTitle
FROM
    (SELECT 

    [Id],
    [AddedDate],
    [AddedBy],
    a.[CategoryId],
    [Title],
    [Abstract],
    [Body],
    b.Title as CategoryTitle ,
ROW_NUMBER() OVER(ORDER BY ' + @sortExpression + ') as RowNum

    FROM  [dbo].[Articles] a INNER JOIN Category b on a.CategoryId = b.Id

    WHERE a.CategoryId = ' + CONVERT(nvarchar(10), @CategoryId) + '
    ) as CategoryIdInfo

If you want to Test you can download the Attachment(Tables and StoredProcedure) Thank you

A: 

Have you tried something like the code below?

This uses the AS keyword to give unique names to columns in your subquery, and aliases to allow unambiguous references to columns within the subquery join itself.

Also note that ALL references to columns that are ambiguous must be aliases, this includes in your case the @sortExpression that you are dynamically injecting.

ALTER PROCEDURE [dbo].[sp_Articles_SelectByCategoryId]   
 @CategoryId int,   
 @startRowIndex int = -1,   
 @maximumRows int = -1,   
 @sortExpression nvarchar(50),   
 @recordCount int = NULL OUTPUT   

AS   

IF (@recordCount IS NOT NULL)   
 BEGIN   
  SET @recordCount = (SELECT COUNT(*) FROM [dbo].[Articles] WHERE [CategoryId] = @CategoryId   
 )   
  RETURN   
 END   

IF LEN(@sortExpression) = 0   
   SET @sortExpression = 'Id'

DECLARE @sql nvarchar(4000) 
SET @sql =  
'SELECT 
 [Id], 
 [AddedDate], 
 [AddedBy], 
 [CategoryId], 
 [Title], 
 -- CategoryTitle refers to the column name declared by that AS inside the subquery
 CategoryTitle
 [Abstract], 
 [Body], 

FROM 
 (SELECT  

 [Id], 
 [AddedDate], 
 [AddedBy], 
 -- The column aliases a and c allow us to refer to similarly names column unambiguously
 a.[CategoryId], 
 a.[Title], 
 c.[Title] as CategoryTitle,
 [Abstract], 
 [Body], 

--- This now includes an alias on the sortExpression order by
ROW_NUMBER() OVER(ORDER BY a.' + @sortExpression + ') as RowNum 

 FROM  [dbo].[Articles] a
 Join [dbo].[Category] c
 ON c.CategoryID = a.CategoryID
 WHERE c.CategoryId = ' + CONVERT(nvarchar(10), @CategoryId) + '

 ) as CategoryIdInfo 
 WHERE  
 ((RowNum between (' + CONVERT(nvarchar(10), @startRowIndex) + ') AND ' + CONVERT(nvarchar(10), @startRowIndex) + ' + ' + CONVERT(nvarchar(10), @maximumRows) + ' - 1)  
   OR ' + CONVERT(nvarchar(10), @startRowIndex) + ' = -1 OR ' + CONVERT(nvarchar(10), @maximumRows) + ' = -1)' 

  -- Execute the SQL query 
  EXEC sp_executesql @sql 

What that does is declare an alias for both the Articles and the Category table (a and c respectively) that allows you to reference their columns unambiguously.

I haven't tried this with the exact case of using Row_Number but can think of no reason why it wouldn't work.

Then using the AS keyword should give you a unique name to use with your outer select.

David Hall
David , I'm not good with sql , But i tried every way that came to my mind , This query Doesn't work . I knew how to use Alias , But the problem is I have two Select (select , , , ... from(,,,,Rownum.....)
Mostafa
Did you see the part of my answer talking about the AS keyword for the title column? I just tried this with a subselect and it works as expected, allowing both title columns to be selected.
David Hall
Also, have you tried running just the inner query, with the Joins? I've always found that breaking things up into smaller queries can help clarify what is going on.
David Hall
I appreciate David .Yes, I have done like you before , But unfortunately this query doesn't work either , by this query i have 5 both Invalid and Ambiguous column name
Mostafa
Could you please post you articles table structure too, as well as the query you are running that is failing with the join and the aliases.
David Hall
David , You have Written :FROM [dbo].[Articles] a Join [dbo].[Category] c ON c.CategoryID = a.CategoryID While my Primary key in Category Table is Id , I fixed that But no answer again
Mostafa
Mostafa
Are there any columns with the same names in the two tables, that you are selecting, that you haven't aliased, or that you are using in your query? You need to use the alias **everywhere** that an ambiguous column is used
David Hall
Mostafa - that is an arabic forum that appears to require a log in.
David Hall
So, have you aliased id column in the select and the joins?
David Hall
Ah! I think I've got it - you are dynamically inserting the Id column in your order by clauses. This will need to be aliased as well.
David Hall
Mostafa
Have you tried the sort expression? What happens when you replace that with a simple order by clause on a.id with no dynamicl sql for now?
David Hall
It's necessary for storedProcedure to have sortExpression Parameter, it could be every thing even "space". When testing just need to pass "space" for sortExpression parameter
Mostafa
Mostafa, in your tables you have the following names in both tables: Id, AddedBy, AddedDate, Title, all of these will need to be aliased when you do the join. Did you alias AddedBy and AddedDate?
David Hall
Also, could you please post the @sql that you execute which is failing. Or do you get the failure before executing?
David Hall
It compile correctly , But error in running . There Error is :Msg 156, Level 15, State 1, Line 12Incorrect syntax near the keyword 'FROM'.Msg 156, Level 15, State 1, Line 33Incorrect syntax near the keyword 'as'.I removed an extra "," after "Body" and before "From" + i edit " ON c.CategoryID = a.CategoryID" to "ON c.CategoryID = a.Id" But still Erros
Mostafa
Dear David , Could you look at the my question again , I edited , and posted a sample code again .
Mostafa
+1  A: 

The following code works perfectly on your provided tables - with no data in them but that shouldn't make a difference since I just want to prove the query execution, not the results.

Things to note with this code:

The sort expression must include an alias

SET @sortExpression = 'a.Id'       

All duplicated column must be aliased

Note how a.[Id], a.[AddedDate], a.[AddedBy], a.[CategoryId], a.[Title], b.[Title] are all aliased

Column names must match between the inner and outer queries

You had a column name of ArticleTitle in your outer select, but a column of CategoryTitle in your inner select. That would never work.

DECLARE @SortExpression nvarchar(100)
SET @sortExpression = 'a.Id'        

DECLARE @sql nvarchar(4000) 
SET @sql =  
'SELECT 
    [Id], 
    [AddedDate], 
    [AddedBy], 
    [CategoryId], 
    [Title], 
    [Abstract], 
    [Body], 
    CategoryTitle 
FROM 
    (SELECT  

    a.[Id], 
    a.[AddedDate], 
    a.[AddedBy], 
    a.[CategoryId], 
    a.[Title], 
    [Abstract], 
    [Body], 
    b.Title as CategoryTitle , 
ROW_NUMBER() OVER(ORDER BY ' + @sortExpression + ') as RowNum 

    FROM  [dbo].[Articles] a INNER JOIN Category b on a.CategoryId = b.Id 

    WHERE a.CategoryId = ' + CONVERT(nvarchar(10), @CategoryId) + ' 
    ) as CategoryIdInfo' 

      -- Execute the SQL query 
  EXEC sp_executesql @sql  
David Hall
David thanks a ton ! You helped me a lot . If i spend one more day i couldn't solve my problem . What do you think about deleting our previous Comments , Because the answer is this post .
Mostafa