SQL Server 2014 with clause inside function/procedure - sql

Is it possible to make a user defined function /user defined procedure with the "with" clause inside it?
CREATE FUNCTION udf_UsersComments (
#Id INT
)
RETURNS #UsersComments TABLE (
CommentTextFormatted NVARCHAR(MAX),
DateCommented NVARCHAR(MAX),
Username NVARCHAR(255),
ParentCommentId INT,
Id INT
)
AS
BEGIN
WITH UpperHierarchy (Id, QuestionId, CommentText, ParentCommentId, DateCommented, UserId, HierarchyOrder,
lineage)
AS (SELECT com.Id,
com.QuestionId,
com.CommentText,
com.ParentCommentId,
com.DateCommented,
com.UserId,
0 AS HierarchyOrder,
Cast ('/' AS VARCHAR(255)) AS Lineage
FROM Comments AS com
WHERE com.ParentCommentId IS NULL AND IsDeleted=0
UNION ALL
(SELECT com.Id,
com.QuestionId,
com.CommentText,
com.ParentCommentId,
com.DateCommented,
com.UserId,
HierarchyOrder + 1,
Cast(lineage + Ltrim(Str(com.ParentCommentId, 6, 0))
+ '/' AS VARCHAR(255))
FROM Comments AS com
INNER JOIN UpperHierarchy AS parent
ON com.ParentCommentId = parent.Id
WHERE com.IsDeleted=0))
SELECT CommentTextFormatted, DateCommented, U.Username, ParentCommentId, Com.id
FROM Questions AS Q
INNER JOIN
(SELECT Space(HierarchyOrder*5) + CommentText AS CommentTextFormatted, Id, QuestionId, ParentCommentId, DateCommented, UserId, lineage
FROM UpperHierarchy) AS Com
ON Com.QuestionId=Q.Id
INNER JOIN Users AS U
ON U.Id=Com.UserId
WHERE Q.Id=#Id
ORDER BY lineage + Ltrim(Str(Q.Id, 6, 0))
RETURN
END
GO
And I am getting this error
Msg 444, Level 16, State 2, Procedure udf_UsersComments, Line 13
Select statements included within a function cannot return data to a
client.

Make it as a Inline table valued function. Check this question to know why I chose inline instead of multi line table valued function
CREATE FUNCTION udf_UsersComments (
#Id INT
)
RETURNS TABLE
AS
Return(
WITH UpperHierarchy (Id, QuestionId, CommentText, ParentCommentId, DateCommented, UserId, HierarchyOrder,
lineage)
AS (SELECT com.Id,
com.QuestionId,
com.CommentText,
com.ParentCommentId,
com.DateCommented,
com.UserId,
0 AS HierarchyOrder,
Cast ('/' AS VARCHAR(255)) AS Lineage
FROM Comments AS com
WHERE com.ParentCommentId IS NULL AND IsDeleted=0
UNION ALL
(SELECT com.Id,
com.QuestionId,
com.CommentText,
com.ParentCommentId,
com.DateCommented,
com.UserId,
HierarchyOrder + 1,
Cast(lineage + Ltrim(Str(com.ParentCommentId, 6, 0))
+ '/' AS VARCHAR(255))
FROM Comments AS com
INNER JOIN UpperHierarchy AS parent
ON com.ParentCommentId = parent.Id
WHERE com.IsDeleted=0))
SELECT CommentTextFormatted, DateCommented, U.Username, ParentCommentId, Com.id,ordercol = lineage + Ltrim(Str(Q.Id, 6, 0))
FROM Questions AS Q
INNER JOIN
(SELECT Space(HierarchyOrder*5) + CommentText AS CommentTextFormatted, Id, QuestionId, ParentCommentId, DateCommented, UserId, lineage
FROM UpperHierarchy) AS Com
ON Com.QuestionId=Q.Id
INNER JOIN Users AS U
ON U.Id=Com.UserId
WHERE Q.Id=#Id)
Note, I have added another column in result to do the ordering while selecting the function. You cannot use Order by without TOP inside a function
select CommentTextFormatted, DateCommented, Username, ParentCommentId, id
from udf_UsersComments(1)--some id
order by ordercol
Regarding your original issue, you are missing insert into #UsersComments. CTE select should insert the records into #UsersComments

Related

The multi-part identifier "company.ID" could not be bound

This query breaks because I have a multipart identifier that could not be bound. I have searched around and have tried a few suggestions online and none of which work for me. I have made sure that there is no typing errors.
WITH TempResult AS
(
SELECT
ROW_NUMBER() OVER(ORDER BY RequestDate) as RowNum,
[BillSearchesId],
[RequestDate],
company.[ID] AS [CompanyId],
users.ID AS [UserID],
[DeedsOffice],
[Description],
[DocNo],
[MicrofilmRefNumber],
[UserReference],
[USERNAME],
[TextCopyRequest],
[NotFound],
STUFF ((SELECT ', ' + CAST([FormatId] AS VARCHAR(20))
FROM [dbo].[DeedsDocumentCopyLocations] ddcl
WHERE ddcl.DocumentCopyId = ddcr.DocumentCopyId
FOR XML PATH('')), 1, 1, '') AS [FormatsAvailable]
FROM
[dbo].[DocumentCopyRequestsTable] ddcr
INNER JOIN
[dbo].[DocumentCopiesTable] ddc ON ddc.id = ddcr.DocumentCopyId
INNER JOIN
[dbo].[USERSTable] users ON users.ID = ddcr.UserId
INNER JOIN
[dbo].[COMPANYTable] company ON company.ID = ddcr.CompanyId
)
SELECT TOP (#LastRec-1) *
FROM TempResult
WHERE RowNum > #FirstRec
AND company.ID = #companyID // The multi-part identifier "company.ID" could not be bound.
AND RowNum < #LastRec
AND DocNo LIKE ISNULL(#DocumentNumber + '%',DocNo)
AND [Description] LIKE ISNULL(#Description + '%',[Description])
AND UserReference LIKE ISNULL(#Reference + '%', UserReference)
There's no table or table alias Company in that SELECT after your CTE - you cannot reference a table that isn't part of your SELECT ....
But that company.ID is available from the CTE already - just use that!
WITH TempResult AS
(
SELECT
....
company.[ID] AS [CompanyId], -- this defines a column "CompanyId" in your CTE
....
FROM
....
)
SELECT TOP (#LastRec-1) *
FROM TempResult
WHERE RowNum > #FirstRec
-- use the "CompanyID" column from your "TempResult" CTE .....
AND CompanyId = #companyID
......

Types don't match between the anchor and the recursive part in column "ColumnName" of recursive query "CTE"

Trying to replicate Concatenation of multiple rows into one string-->https://www.red-gate.com/simple-talk/sql/t-sql-programming/concatenating-row-values-in-transact-sql/#Toc205129485
here is the query:
WITH CTE (id, CodeList, Code, Length)
AS (SELECT CAST(d.id AS NVARCHAR(MAX)) AS Id,
CAST('' AS NVARCHAR(MAX)) AS CodeList,
CAST('' AS NVARCHAR(MAX)) AS Code,
0 AS Length
FROM Letters d
INNER JOIN Cat c
ON c.ID = Cat_Sub
GROUP BY d.id
UNION ALL
SELECT CAST(dp.id AS NVARCHAR(MAX)) AS Id,
CAST(
CAST(CodeList AS NVARCHAR(MAX))
+ CASE
WHEN CAST(Length AS NVARCHAR(MAX)) = CAST('0' AS NVARCHAR(MAX)) THEN
CAST('' AS NVARCHAR(MAX))
ELSE
CAST(', ' AS NVARCHAR(MAX))
END
?????+ CAST(cp.Code AS NVARCHAR(MAX))??????
AS NVARCHAR(MAX)
) AS CodeList,
CAST(c.Code AS NVARCHAR(MAX)) AS Code,
c.Length + 1
FROM CTE c
INNER JOIN Letters dp
ON c.id= dp.id
INNER JOIN Cat cp
ON cp.ID = dp.id
WHERE cp.Code > c.Code
)
SELECT *
FROM CTE;
Msg 240, Level 16, State 1, Line 1 Types don't match between the
anchor and the recursive part in column "CodeList" of recursive query
"CTE".
I know this is a very often asked question but I can't get from where I get my error.
After testing I discovered that it comes when I add a piece of code in between question marks.
It has been casted but it still gives an error
Finally, I have solved the problem with another logic:
WITH Ranked ( CategoryId, rnk, ProductName )
AS ( SELECT CategoryId,
ROW_NUMBER() OVER( PARTITION BY CategoryId ORDER BY CategoryId ),
CAST( ProductName AS VARCHAR(8000) )
FROM Northwind..Products),
AnchorRanked ( CategoryId, rnk, ProductName )
AS ( SELECT CategoryId, rnk, ProductName
FROM Ranked
WHERE rnk = 1 ),
RecurRanked ( CategoryId, rnk, ProductName )
AS ( SELECT CategoryId, rnk, ProductName
FROM AnchorRanked
UNION ALL
SELECT Ranked.CategoryId, Ranked.rnk,
RecurRanked.ProductName + ', ' + Ranked.ProductName
FROM Ranked
INNER JOIN RecurRanked
ON Ranked.CategoryId = RecurRanked.CategoryId
AND Ranked.rnk = RecurRanked.rnk + 1 )
SELECT CategoryId, MAX( ProductName )
FROM RecurRanked
GROUP BY CategoryId;

Delete the same reference multiple columns rows safely?

I need to find all the parent-children relationships, which are all linked to my primary column ID
How I can delete the same reference columns in the table? Let say for example,if I want to delete "Google", I have to delete "HP" and Intel first also the child of HP as well.
I have tried the below thus far, but that works with only one column.
WITH tb (id,Name, Level, Path, Parent)
AS
(
SELECT
id,Name, 1 AS Level,
CAST('/'+Name as nvarchar(max)) as Path,
CAST(NULL as nvarchar(max)) as Parent
FROM krishtest
WHERE parent1 IS NULL
UNION All
SELECT
e.id,e.Name, x.Level + 1 AS Level, x.Path + '/' + e.Name as Path,
REVERSE(SUBSTRING( REVERSE(x.[Path]) ,0 , CHARINDEX( '/', REVERSE(x.[Path])) )) as [Parent]
FROM krishtest e
JOIN tb x ON x.id = e.parent1
)
SELECT Name, Level, Path, Parent FROM tb
is this use full?
declare #tmp table (id int, Name varchar(10),Parent1 int,Parent2 int,Parent3 int,Parent4 int,Parent5 int)
insert into #tmp
SELECT 1,'Microsoft',NULL,NULL,NULL,NULL,NULL
union
SELECT 2,'Google',1,NULL,NULL,NULL,NULL
union
SELECT 3,'HP',NULL,2,NULL,NULL,NULL
union
SELECT 4,'Amazone',NULL,NULL,3,NULL,NULL
union
SELECT 5,'FB',NULL,NULL,NULL,4,NULL
union
SELECT 6,'Yahoo',NULL,NULL,NULL,4,NULL
union
SELECT 7,'Intel',NULL,NULL,2,NULL,NULL
union
SELECT 8,'Apple',7,5,NULL,NULL,NULL
select * from #tmp
;with name_tree as (
select *
from #tmp
where id = 2
union all
select c.*
from #tmp c
join name_tree p on (p.id = c.parent1 or p.id = c.parent2 or p.id = c.parent3 or p.id = c.parent4 or p.id = c.parent5)
)
delete from t
from #tmp t
JOIN name_tree c on t.id=c.id
select * from #tmp
I haven't provided an actual solution to your issue here. But I would recommend investigating recursive Common Table Expressions. That should allow you to find all the parent records, then you can run a delete on them.
https://technet.microsoft.com/en-us/library/ms186243(v=sql.105).aspx
You can simply modify the recursive CTE's where clause like in below query to get all rows that need to be deleted.
See live demo
create table krishtest (id int, name varchar(100), parent1 int, parent2 int)
insert into krishtest values
(1,'Microsoft', NULL, NULL),
(2,'Google',1,NULL),
(3,'HP',NULL,2),
(4,'amazon',3,NULL),
(5,'FB',NULL,4),
(6,'yahoo',3,NULL),
(7,'cisco',6,NULL)
;
WITH tb (id,Name, Level, Path, Parent)
AS
(
SELECT
id,Name, 1 AS Level,
CAST('/'+Name as nvarchar(max)) as Path,
CAST(NULL as nvarchar(max)) as Parent
FROM krishtest
WHERE -- COALESCE(parent1,parent2) IS NULL
name ='HP'
UNION All
SELECT
e.id,e.Name, x.Level + 1 AS Level, x.Path + '/' + e.Name as Path,
REVERSE(SUBSTRING( REVERSE(x.[Path]) ,0 , CHARINDEX( '/', REVERSE(x.[Path])) )) as [Parent]
FROM krishtest e
JOIN tb x ON x.id = COALESCE(e.parent1,e.parent2)
)
--delete FROM krishtest where id in( select id from tb)
--select * from krishtest
SELECT Name, Level, Path, Parent FROM tb

Passing column value in stored procedure in sql

Create Proc [dbo].[SprptAssessmentCompartion_Subject]
#intQstnMasterID int,
#intAssessmentID int,
#intQstnMasterID1 int,
#intAssessmentID1 int,
#intUserID int
As
Begin
select DISTINCT vchSubject,intUserID,SUM(FrstId) as 'FrstId' ,SUM(ScndId) as 'ScndId' from
( SELECT
intSubjectID,
intUserID,
ISNULL([#intAssessmentID],0) as 'FrstId',
ISNULL([#intAssessmentID1],0) as 'ScndId',
FinalsCORE
FROM
(
select DISTINCT intSubjectID,intUserID,intAssignmnetID,SUM(KidTtl) as 'KidScore',COUNT(intSubjectID) AS 'FinalsCORE' ,(SUM(KidTtl) /COUNT(intSubjectID)*100) as 'Ct' from
(
select Answetble.vchAssignmentName ,intAssignmnetID,intUserID,intSlNo,isnull(QuiestionPaper.vchTopic,'-') as 'VchTopic',isnull(Answetble.fltMark,0) as 'KidTtl' ,QuiestionPaper.intSubjectID from QuiestionMapping
inner join QuiestionPaper on QuiestionPaper.intQstnID=QuiestionMapping.intQstnID
Left outer join
(
select distinct intQstnID,intAssessmentID,intAssignmnetID,intUserID,intQstnMasterID,fltMark,fltTotalMark,intAssignedByUserID,vchAssignmentName from
(
SELECT * from ManageAssessment
inner join Assignment ON Assignment.intAssignmnetID=ManageAssessment.intAssessmentID) as B
INNER JOIN UserMapping ON UserMapping.intPTUserID= intAssignedByUserID
where intQstnMasterID=#intQstnMasterID and intAssessmentID=#intAssessmentID AND intPTUserID=#intUserID
OR( intQstnMasterID=#intQstnMasterID1 and intAssessmentID=#intAssessmentID1 AND intPTUserID=#intUserID)
)
as Answetble on Answetble.intQstnID=QuiestionPaper.intQstnID
where QuiestionMapping.intQstnMasterID=#intQstnMasterID OR QuiestionMapping.intQstnMasterID=#intQstnMasterID1
)as A
GROUP BY intAssignmnetID,intSubjectID,intUserID
)
AS t
PIVOT
(
Max(Ct)
FOR intAssignmnetID IN( [#intAssessmentID] ,[#intAssessmentID1])
) AS p
)as B
inner join subject on Subject.intSubjectID=b.intSubjectID
group by subject.vchSubject,B.intUserID
End
Here #intAssessmentID is one of the columns when I use this in stored procedure I will get error
Msg 8114, Level 16, State 1, Procedure SprptAssessmentCompartion_Subject, Line 47
Error converting data type nvarchar to int.
Msg 473, Level 16, State 1, Procedure SprptAssessmentCompartion_Subject, Line 47
The incorrect value "#intAssessmentID" is supplied in the PIVOT operator.
Can anyone help me to resolve the error?
You cannot specify columns for PIVOT this way, you have to use dynamic SQL.
Create Proc [dbo].[SprptAssessmentCompartion_Subject]
#intQstnMasterID int,
#intAssessmentID int,
#intQstnMasterID1 int,
#intAssessmentID1 int,
#intUserID int
As
Begin
declare #sql as nvarchar(max) = N'select DISTINCT vchSubject,intUserID,SUM(FrstId) as ''FrstId'' ,SUM(ScndId) as ''ScndId'' from
( SELECT
intSubjectID,
intUserID,
ISNULL([' + CONVERT(NVARCHAR(20), #intAssessmentID) + N'],0) as ''FrstId'',
ISNULL([' + CONVERT(NVARCHAR(20), #intAssessmentID1) + N'],0) as ''ScndId'',
FinalsCORE
FROM
(
select DISTINCT intSubjectID,intUserID,intAssignmnetID,SUM(KidTtl) as ''KidScore'',COUNT(intSubjectID) AS ''FinalsCORE'' ,(SUM(KidTtl) /COUNT(intSubjectID)*100) as ''Ct'' from
(
select Answetble.vchAssignmentName ,intAssignmnetID,intUserID,intSlNo,isnull(QuiestionPaper.vchTopic,''-'') as ''VchTopic'',isnull(Answetble.fltMark,0) as ''KidTtl'' ,QuiestionPaper.intSubjectID from QuiestionMapping
inner join QuiestionPaper on QuiestionPaper.intQstnID=QuiestionMapping.intQstnID
Left outer join
(
select distinct intQstnID,intAssessmentID,intAssignmnetID,intUserID,intQstnMasterID,fltMark,fltTotalMark,intAssignedByUserID,vchAssignmentName from
(
SELECT * from ManageAssessment
inner join Assignment ON Assignment.intAssignmnetID=ManageAssessment.intAssessmentID) as B
INNER JOIN UserMapping ON UserMapping.intPTUserID= intAssignedByUserID
where intQstnMasterID=#intQstnMasterID and intAssessmentID=#intAssessmentID AND intPTUserID=#intUserID
OR( intQstnMasterID=#intQstnMasterID1 and intAssessmentID=#intAssessmentID1 AND intPTUserID=#intUserID)
)
as Answetble on Answetble.intQstnID=QuiestionPaper.intQstnID
where QuiestionMapping.intQstnMasterID=#intQstnMasterID OR QuiestionMapping.intQstnMasterID=#intQstnMasterID1
)as A
GROUP BY intAssignmnetID,intSubjectID,intUserID
)
AS t
PIVOT
(
Max(Ct)
FOR intAssignmnetID IN( [' + CONVERT(NVARCHAR(20), #intAssessmentID) + N'] ,[' + CONVERT(NVARCHAR(20), #intAssessmentID1) + N'])
) AS p
)as B
inner join subject on Subject.intSubjectID=b.intSubjectID
group by subject.vchSubject,B.intUserID'
exec sp_executesql #sql
End

Comma Separated SQL Server Result Set 'JOINED' with other Columns

I have a table say ProjectMaster:
Id ProjectName
1 A
2 B
3 C
another table ProjectMeter
Id ProjectId MeterNumber
1 1 #0001
2 1 #0002
3 1 #0003
4 2 #0004
5 2 #0005
6 3 #0006
I wish to have following output
ProjectName MeterNumbers
A #0001, #0002, #0003
B #0004, #0005
C #0006
I tried this and this, but unable to solve my problem.
I cannot use a table variable.
I have a already written Stored Procedure and it brings data from many joined tables. ProjectMaster also happens to be joined in one of these tables. Now am required to fetch data from ProjectMeter, such that, each row has concatenated ProjectMeter.MeterNumber corresponding to the ProjectId in that column.
right now, I get concatenated list of all meternumbers in all the rows.
I cannot use CURSOR, TABLE variable , Temp TABLE
( I hope still something can be done to my cause)
please help.....
Try this:
SELECT projectname, STUFF((SELECT distinct ', ' + meternumber
from projectmeter m
where p.id = m.projectid
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'') MeterNumbers
from projectmaster p
See SQL Fiddle with Demo
DECLARE #ProjectMaster AS TABLE
(
ID INT IDENTITY(1, 1) ,
ProjectName VARCHAR(2)
)
DECLARE #ProjectMeter AS TABLE
(
ID INT IDENTITY(1, 1) ,
ProjectID INT ,
MeterNumber VARCHAR(50)
)
INSERT INTO #ProjectMaster
( ProjectName )
VALUES ( 'A' )
INSERT INTO #ProjectMeter
( ProjectID, MeterNumber )
VALUES ( 1, '#0001' )
INSERT INTO #ProjectMeter
( ProjectID, MeterNumber )
VALUES ( 1, '#0002' )
SELECT pMaster.ID, STUFF(( SELECT ',' + MeterNumber
FROM #ProjectMeter
FOR
XML PATH('')
), 1, 1, '') AS 'Concat Result'
FROM #ProjectMeter pMeter
INNER JOIN #ProjectMaster pMaster ON pMaster.ID = pMeter.ProjectID
GROUP BY pMaster.ID
I have used table variables here but surely you just need to drop the #'s as I have used the same table names as you have specified? Not sure if this is okay? :)
Also in MS SQL you can do it using recursive query with CTE.
Here is a SQLFiddle demo
;with t1 as (
select t.*,
cast(meternumber as varchar(max)) as m2,
0 as level
from ProjectMeter t
where not exists
(select id
from ProjectMeter l
where l.id<t.id and l.ProjectId=t.ProjectID
)
union all
select b.*,
cast(c.m2+','+b.MeterNumber as varchar(max)) as m2,
c.level+1 as level
from ProjectMeter b
inner join t1 c
on (c.id < b.id) and (b.ProjectID=c.ProjectId)
)
select pm.ProjectName as ProjectName,
t1.m2 as MeterNumbers
from t1
inner join
(select ProjectId,max(level) ml
from t1
group by ProjectId
) t2
on (t1.ProjectId=t2.ProjectID) and (t1.level=t2.ml)
left join ProjectMaster pm
on (t1.ProjectId=pm.Id)
order by t1.ProjectID