I am trying to make a CTE with 3 levels. Can this be done?
I am using this SQL statement for 2 levels. Can you help me to add a third level?
WITH ProjectReport(ParentProject, ProjectNr, [Level]) AS
(
SELECT ParentProject, Projectnr, 0 as [Level]
FROM prproject
WHERE ParentProject IS NULL
UNION ALL
SELECT e.ParentProject, e.ProjectNr, [Level]+1
FROM PrProject AS e
INNER JOIN ProjectReport AS d
ON e.ParentProject = d.ProjectNr
)
SELECT ParentProject, ProjectNr, [Level]
FROM ProjectReport
WHERE [Level] <= 2 and ParentProject = 'cl3264';
It doesn't look like you really need a recursive CTE for this. It seems like 2 joins would do the trick (depending on what tables you have).
SELECT A.ProjectNr
, B.ProjectNr
, C.ProjectNr
FROM PROJECTS A
INNER JOIN PROJECTS B ON A.ProjectNr = B.ParentProject
INNER JOIN PROJECTS C ON B.ProjectNr = C.ParentProject
WHERE A.ProjectNr = 'CL3264'
This should also work even if you need to join the same table.
You can use the below script to check out the results:
CREATE TABLE #PROJECTS (ProjectNr varchar(30), ParentProject varchar(30));
INSERT INTO #PROJECTS (ProjectNr, ParentProject) values ('CL3264', NULL)
, ('CL3264-B', 'Cl3264')
, ('CL3264-C1', 'Cl3264')
, ('CL3264-C2', 'Cl3264')
, ('CL3264-C3', 'Cl3264')
, ('CL3264-F1', 'Cl3264')
, ('CL3264-G1', 'Cl3264')
, ('CL3264-G2', 'Cl3264')
, ('CL3264-P', 'Cl3264')
, ('PR1700000468', 'CL3264-B')
, ('PR1700000469', 'CL3264-C1')
, ('PR1700000474', 'CL3264-C2')
, ('PR1700000475', 'CL3264-C3')
, ('PR1700000476', 'CL3264-F1')
, ('PR1700000477', 'CL3264-G1')
, ('PR1700000478', 'CL3264-G2')
, ('PR1700000479', 'CL3264-P')
, ('PR1700000999', 'CL3264-X')
, ('PR1700009999', 'CL3264-Y')
, ('CL3264-Y', 'CL2360') -- purposely added to see that the filtering works
, ('CL3264-X', 'CL2360') -- purposely added to see that the filtering works
, ('CL2360', NULL) -- purposely added to see that the filtering works
SELECT A.ProjectNr
, B.ProjectNr AS Project_Level2
, C.ProjectNr AS Project_Level3
FROM #PROJECTS A
INNER JOIN #PROJECTS B ON A.ProjectNr = B.ParentProject
INNER JOIN #PROJECTS C ON B.ProjectNr = C.ParentProject
WHERE A.ProjectNr = 'CL3264'
You can see the output of the script / query below:
You can have multiple CTEs in one query, as well as reuse a CTE:
WITH CTE1 AS (
SELECT TOP 2 Name FROM Sales.Store
),
CTE2 AS (
SELECT TOP 2 ProductNumber, Name FROM Production.Product
),
CTE3 AS (
SELECT TOP 2 Name FROM Person.ContactType
)
SELECT * FROM CTE1,CTE2,CTE3
-- Or use INNER JOIN, LEFT JOIN instead of Cartesian Joins
Related
I am writing a query to look through and get two seperate averages based on where conditions.
I tried two select statetments but ended up with lots of duplicates.
Now I have a union which works pretty well, although I have my two fields in alternating rows instead of seperate columns.
Can anyone suggest a fix, sorry for the dodgy code!
SELECT
tblSkillName.skillName,
tblTestScores.skillUID,
AVG(tblTestScores.percentage) AS `cohortPercentage`
FROM
(
(
(
tblTestScores
INNER JOIN tblUsers ON tblUsers.email = tblTestScores.email
)
INNER JOIN tblTestDetails ON tblTestScores.testDetailsID = tblTestDetails.testDetailsID
)
INNER JOIN tblSkillName ON tblSkillName.skillUID = tblTestScores.skillUID
)
WHERE
teacherGroup = '9JS2/Cp'
AND tblTestScores.testDetailsID = 1
GROUP BY
skillName
UNION ALL
SELECT
tblSkillName.skillName,
tblTestScores.skillUID,
AVG(tblTestScores.percentage) AS `groupPercentage`
FROM
(
(
(
tblTestScores
INNER JOIN tblUsers ON tblUsers.email = tblTestScores.email
)
INNER JOIN tblTestDetails ON tblTestScores.testDetailsID = tblTestDetails.testDetailsID
)
INNER JOIN tblSkillName ON tblSkillName.skillUID = tblTestScores.skillUID
)
WHERE
tblTestScores.testDetailsID = 1
GROUP BY
skillName
ORDER BY
skillUID ASC
I need to get the following code to work, however, it is giving me an error at the last Select statement. The error that I am receiving is:
Incorrect syntax near the keyword 'select'.
Could someone please assist in resolving this issue? Thank You.
declare #Lookup table(
Id int identity(1, 1)
, SongTitle nvarchar(512)
)
insert into #Lookup(SongTitle)
select *
from (
values('Deuce')
, ('Strutter')
, ('Black_Diamond')
, ('Parasite')
, ('Strange_Ways')
, ('Rock_Bottom')
, ('God_of_Thunder')
, ('Love_Gun')
, ('She')
, ('I_Stole_Your_Love')
)
select Albums.AlbumName
, Songs.SongTitle
, Songs.Writers
, Songs.Vocals
, Songs.SID
, Songs.TheTime
from Albums A
inner join Songs S
on A.AID = S.AID
inner join #Lookup L
on L.SongTitle = S.SongTitle
order by L.Id
You need to provide an alias for your derived tables
insert into #Lookup(SongTitle)
select *
from (
values('Deuce')
, ('Strutter')
, ('Black_Diamond')
, ('Parasite')
, ('Strange_Ways')
, ('Rock_Bottom')
, ('God_of_Thunder')
, ('Love_Gun')
, ('She')
, ('I_Stole_Your_Love')
) AS v; -- <<< provide alias for derived tables
If you assign an alias to a table, use the alias instead of the table name.
select A.AlbumName -- <<< use alias instead of table name
, S.SongTitle -- <<< use alias instead of table name
, S.Writers
, S.Vocals
, S.SID
, S.TheTime
from Albums A
inner join Songs S
on A.AID = S.AID
inner join #Lookup L
on L.SongTitle = S.SongTitle
order by L.Id;
I'm trying to join three tables to pull back a list of distinct blog posts with associated assets (images etc) but I keep coming up a cropper. The three tablets are tblBlog, tblAssetLink and tblAssets. The Blog tablet hold the blog, the asset table holds the assets and the Assetlink table links the two together.
tblBlog.BID is the PK in blog, tblAssets.AID is the PK in Assets.
This query works but pulls back multiple posts for the same record. I've tried to use select distinct and group by and even union but as my knowledge is pretty poor with SQL - they all error.
I'd like to also discount any assets that are marked as deleted (tblAssets.Deleted = true) but not hide the associated Blog post (if that's not marked as deleted). If anyone can help - it would be much appreciated! Thanks.
Here's my query so far....
SELECT dbo.tblBlog.BID,
dbo.tblBlog.DateAdded,
dbo.tblBlog.PMonthName,
dbo.tblBlog.PDay,
dbo.tblBlog.Header,
dbo.tblBlog.AddedBy,
dbo.tblBlog.PContent,
dbo.tblBlog.Category,
dbo.tblBlog.Deleted,
dbo.tblBlog.Intro,
dbo.tblBlog.Tags,
dbo.tblAssets.Name,
dbo.tblAssets.Description,
dbo.tblAssets.Location,
dbo.tblAssets.Deleted AS Expr1,
dbo.tblAssetLink.Priority
FROM dbo.tblBlog
LEFT OUTER JOIN dbo.tblAssetLink
ON dbo.tblBlog.BID = dbo.tblAssetLink.BID
LEFT OUTER JOIN dbo.tblAssets
ON dbo.tblAssetLink.AID = dbo.tblAssets.AID
WHERE ( dbo.tblBlog.Deleted = 'False' )
ORDER BY dbo.tblAssetLink.Priority, tblBlog.DateAdded DESC
EDIT
Changed the Where and the order by....
Expected output:
tblBlog.BID = 123
tblBlog.DateAdded = 12/04/2015
tblBlog.Header = This is a header
tblBlog.AddedBy = Persons name
tblBlog.PContent = *text*
tblBlog.Category = Category name
tblBlog.Deleted = False
tblBlog.Intro = *text*
tblBlog.Tags = Tag, Tag, Tag
tblAssets.Name = some.jpg
tblAssets.Description = Asset desc
tblAssets.Location = Location name
tblAssets.Priority = True
Use OUTER APPLY:
DECLARE #b TABLE ( BID INT )
DECLARE #a TABLE ( AID INT )
DECLARE #ba TABLE
(
BID INT ,
AID INT ,
Priority INT
)
INSERT INTO #b
VALUES ( 1 ),
( 2 )
INSERT INTO #a
VALUES ( 1 ),
( 2 ),
( 3 ),
( 4 )
INSERT INTO #ba
VALUES ( 1, 1, 1 ),
( 1, 2, 2 ),
( 2, 1, 1 ),
( 2, 2, 2 )
SELECT *
FROM #b b
OUTER APPLY ( SELECT TOP 1
a.*
FROM #ba ba
JOIN #a a ON a.AID = ba.AID
WHERE ba.BID = b.BID
ORDER BY Priority
) o
Output:
BID AID
1 1
2 1
Something like:
SELECT b.BID ,
b.DateAdded ,
b.PMonthName ,
b.PDay ,
b.Header ,
b.AddedBy ,
b.PContent ,
b.Category ,
b.Deleted ,
b.Intro ,
b.Tags ,
o.Name ,
o.Description ,
o.Location ,
o.Deleted AS Expr1 ,
o.Priority
FROM dbo.tblBlog b
OUTER APPLY ( SELECT TOP 1
a.* ,
al.Priority
FROM dbo.tblAssetLink al
JOIN dbo.tblAssets a ON al.AID = a.AID
WHERE b.BID = al.BID
ORDER BY al.Priority
) o
WHERE b.Deleted = 'False'
You cannot join three tables unless they all have the same attribute. It would work if all tables had BID, but the second join is trying to join AID. Which wont work. They all have to have BID.
Based on your comments
i would like to get is just one asset per blog post (top one ordered
by Priority)
You can change your query as following. I suggest changing the join with dbo.tblAssetLink to filtered one, which contains only one (highest priority) link for every blog.
SELECT dbo.tblBlog.BID,
dbo.tblBlog.DateAdded,
dbo.tblBlog.PMonthName,
dbo.tblBlog.PDay,
dbo.tblBlog.Header,
dbo.tblBlog.AddedBy,
dbo.tblBlog.PContent,
dbo.tblBlog.Category,
dbo.tblBlog.Deleted,
dbo.tblBlog.Intro,
dbo.tblBlog.Tags,
dbo.tblAssets.Name,
dbo.tblAssets.Description,
dbo.tblAssets.Location,
dbo.tblAssets.Deleted AS Expr1,
dbo.tblAssetLink.Priority
FROM dbo.tblBlog
LEFT OUTER JOIN
(SELECT BID, AID,
ROW_NUMBER() OVER (PARTITION BY BID ORDER BY [Priority] DESC) as N
FROM dbo.tblAssetLink) AS filteredAssetLink
ON dbo.tblBlog.BID = filteredAssetLink.BID
LEFT OUTER JOIN dbo.tblAssets
ON filteredAssetLink.AID = dbo.tblAssets.AID
WHERE dbo.tblBlog.Deleted = 'False' AND filteredAssetLink.N = 1
ORDER BY tblBlog.DateAdded DESC
The following script is very slow when its run.
I have no idea how to improve the performance of the script.
Even with a view takes more than quite a lot minutes.
Any idea please share to me.
SELECT DISTINCT
( id )
FROM ( SELECT DISTINCT
ct.id AS id
FROM [Customer].[dbo].[Contact] ct
LEFT JOIN [Customer].[dbo].[Customer_ids] hnci ON ct.id = hnci.contact_id
WHERE hnci.customer_id IN (
SELECT DISTINCT
( [Customer_ID] )
FROM [Transactions].[dbo].[Transaction_Header]
WHERE actual_transaction_date > '20120218' )
UNION
SELECT DISTINCT
contact_id AS id
FROM [Customer].[dbo].[Restaurant_Attendance]
WHERE ( created > '2012-02-18 00:00:00.000'
OR modified > '2012-02-18 00:00:00.000'
)
AND ( [Fifth_Floor_London] = 1
OR [Fourth_Floor_Leeds] = 1
OR [Second_Floor_Bristol] = 1
)
UNION
SELECT DISTINCT
( ct.id )
FROM [Customer].[dbo].[Contact] ct
INNER JOIN [Customer].[dbo].[Wifinity_Devices] wfd ON ct.wifinity_uniqueID = wfd.[CustomerUniqueID]
AND startconnection > '2012-02-17'
UNION
SELECT DISTINCT
comdt.id AS id
FROM [Customer].[dbo].[Complete_dataset] comdt
LEFT JOIN [Customer].[dbo].[Aggregate_Spend_Counts] agsc ON comdt.id = agsc.contact_id
WHERE agsc.contact_id IS NULL
AND ( opt_out_Mail <> 1
OR opt_out_email <> 1
OR opt_out_SMS <> 1
OR opt_out_Mail IS NULL
OR opt_out_email IS NULL
OR opt_out_SMS IS NULL
)
AND ( address_1 IS NOT NULL
OR email IS NOT NULL
OR mobile IS NOT NULL
)
UNION
SELECT DISTINCT
( contact_id ) AS id
FROM [Customer].[dbo].[VIP_Card_Holders]
WHERE VIP_Card_number IS NOT NULL
) AS tbl
Wow, where to start...
--this distinct does nothing. Union is already distinct
--SELECT DISTINCT
-- ( id )
--FROM (
SELECT DISTINCT [Customer_ID] as ID
FROM [Transactions].[dbo].[Transaction_Header]
where actual_transaction_date > '20120218' )
UNION
SELECT
contact_id AS id
FROM [Customer].[dbo].[Restaurant_Attendance]
-- not sure that you are getting the date range you want. Should these be >=
-- if you want everything that occurred on the 18th or after you want >= '2012-02-18 00:00:00.000'
-- if you want everything that occurred on the 19th or after you want >= '2012-02-19 00:00:00.000'
-- the way you have it now, you will get everything on the 18th unless it happened exactly at midnight
WHERE ( created > '2012-02-18 00:00:00.000'
OR modified > '2012-02-18 00:00:00.000'
)
AND ( [Fifth_Floor_London] = 1
OR [Fourth_Floor_Leeds] = 1
OR [Second_Floor_Bristol] = 1
)
-- all of this does nothing because we already have every id in the contact table from the first query
-- UNION
-- SELECT
-- ( ct.id )
-- FROM [Customer].[dbo].[Contact] ct
-- INNER JOIN [Customer].[dbo].[Wifinity_Devices] wfd ON ct.wifinity_uniqueID = wfd.[CustomerUniqueID]
-- AND startconnection > '2012-02-17'
UNION
-- cleaned this up with isnull function and coalesce
SELECT
comdt.id AS id
FROM [Customer].[dbo].[Complete_dataset] comdt
LEFT JOIN [Customer].[dbo].[Aggregate_Spend_Counts] agsc ON comdt.id = agsc.contact_id
WHERE agsc.contact_id IS NULL
AND ( isnull(opt_out_Mail,0) <> 1
OR isnull(opt_out_email,0) <> 1
OR isnull(opt_out_SMS,0) <> 1
)
AND coalesce(address_1 , email, mobile) IS NOT NULL
UNION
SELECT
( contact_id ) AS id
FROM [Customer].[dbo].[VIP_Card_Holders]
WHERE VIP_Card_number IS NOT NULL
-- ) AS tbl
Where exists is generally faster than in as well.
Or conditions are generally slower as well, use more union statements instead.
And learn to use left joins correctly. If you have a where condition (other than where id is null) on the table on teh right side of a left join, it will convert to an inner join. If this is not what you want, then your code is currently giving you an incorrect result set.
See http://wiki.lessthandot.com/index.php/WHERE_conditions_on_a_LEFT_JOIN for an explanation of how to fix.
As stated in a comment optimize one at a time. See which one takes the longest and focus on that one.
union will remove duplicates so you don't need the distinct on the individual queries
On you first I would try this:
The left join is killed by the WHERE hnci.customer_id IN so you might as well have a join.
The sub-query is not efficient as cannot use an index on the IN.
The query optimizer does not know what in ( select .. ) will return so it cannot optimize use of indexes.
SELECT ct.id AS id
FROM [Customer].[dbo].[Contact] ct
JOIN [Customer].[dbo].[Customer_ids] hnci
ON ct.id = hnci.contact_id
JOIN [Transactions].[dbo].[Transaction_Header] th
on hnci.customer_id = th.[Customer_ID]
and th.actual_transaction_date > '20120218'
On that second join the query optimizer has the opportunity of which condition to apply first. Let say [Customer].[dbo].[Customer_ids].[customer_id] and [Transactions].[dbo].[Transaction_Header] each have indexes. The query optimizer has the option to apply that before [Transactions].[dbo].[Transaction_Header].[actual_transaction_date].
If [actual_transaction_date] is not indexed then for sure it would do the other ID join first.
With your in ( select ... ) the query optimizer has no option but to apply the actual_transaction_date > '20120218' first. OK some times query optimizer is smart enough to use an index inside the in outside the in but why make it hard for the query optimizer. I have found the query optimizer make better decisions if you make the decisions easier.
A join on a sub-query has the same problem. You take options away from the query optimizer. Give the query optimizer room to breathe.
try this, temptable should help you:
IF OBJECT_ID('Tempdb..#Temp1') IS NOT NULL
DROP TABLE #Temp1
--Low perfomance because of using "WHERE hnci.customer_id IN ( .... ) " - loop join must be
--and this "where" condition will apply to two tables after left join,
--so result will be same as with two inner joints but with bad perfomance
--SELECT DISTINCT
-- ct.id AS id
--INTO #temp1
--FROM [Customer].[dbo].[Contact] ct
-- LEFT JOIN [Customer].[dbo].[Customer_ids] hnci ON ct.id = hnci.contact_id
--WHERE hnci.customer_id IN (
-- SELECT DISTINCT
-- ( [Customer_ID] )
-- FROM [Transactions].[dbo].[Transaction_Header]
-- WHERE actual_transaction_date > '20120218' )
--------------------------------------------------------------------------------
--this will give the same result but with better perfomance then previouse one
--------------------------------------------------------------------------------
SELECT DISTINCT
ct.id AS id
INTO #temp1
FROM [Customer].[dbo].[Contact] ct
JOIN [Customer].[dbo].[Customer_ids] hnci ON ct.id = hnci.contact_id
JOIN ( SELECT DISTINCT
( [Customer_ID] )
FROM [Transactions].[dbo].[Transaction_Header]
WHERE actual_transaction_date > '20120218'
) T ON hnci.customer_id = T.[Customer_ID]
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
INSERT INTO #temp1
( id
)
SELECT DISTINCT
contact_id AS id
FROM [Customer].[dbo].[Restaurant_Attendance]
WHERE ( created > '2012-02-18 00:00:00.000'
OR modified > '2012-02-18 00:00:00.000'
)
AND ( [Fifth_Floor_London] = 1
OR [Fourth_Floor_Leeds] = 1
OR [Second_Floor_Bristol] = 1
)
INSERT INTO #temp1
( id
)
SELECT DISTINCT
( ct.id )
FROM [Customer].[dbo].[Contact] ct
INNER JOIN [Customer].[dbo].[Wifinity_Devices] wfd ON ct.wifinity_uniqueID = wfd.[CustomerUniqueID]
AND startconnection > '2012-02-17'
INSERT INTO #temp1
( id
)
SELECT DISTINCT
comdt.id AS id
FROM [Customer].[dbo].[Complete_dataset] comdt
LEFT JOIN [Customer].[dbo].[Aggregate_Spend_Counts] agsc ON comdt.id = agsc.contact_id
WHERE agsc.contact_id IS NULL
AND ( opt_out_Mail <> 1
OR opt_out_email <> 1
OR opt_out_SMS <> 1
OR opt_out_Mail IS NULL
OR opt_out_email IS NULL
OR opt_out_SMS IS NULL
)
AND ( address_1 IS NOT NULL
OR email IS NOT NULL
OR mobile IS NOT NULL
)
INSERT INTO #temp1
( id
)
SELECT DISTINCT
( contact_id ) AS id
FROM [Customer].[dbo].[VIP_Card_Holders]
WHERE VIP_Card_number IS NOT NULL
SELECT DISTINCT
id
FROM #temp1 AS T
this is the stored procdure, I need to select Distinct Records and display them in a random order but I am facing an error that selecting Distinct can not be used with newid(), so how can I walk around this ?
USE [OtlobODR]
GO
/****** Object: StoredProcedure [OtlobFood].[ListOffersItems] Script Date: 11/18/2012 13:01:34 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER procedure [OtlobFood].[ListOffersItems]
#Fk_CampaignID int
as
select distinct
CampaignID, CampaignOffers.OldPrice
, dbo.ItemSizes.IS_Id,
, dbo.Items.[Item_Description_L2] as Item_Description
, dbo.Items.[Item_Image]
, dbo.Items.[Item_Details]
, dbo.ItemSizes.[IS_Price] as Price
-- if null then it is not a featured meal
, dbo.ProviderItems.[PI_Id] as ProviderItems_PI_ID
, dbo.ItemCategories.[ItemCat_Id]
, dbo.Providers.Provider_Name_L2 as Provider_Name
, dbo.Providers.Provider_Menu_Logo
, dbo.Providers.Provider_Id
FROM Items
INNER JOIN ProviderItems ON Items.Item_Id = ProviderItems.Item_Id
INNER JOIN dbo.ItemSizes ON dbo.Items.Item_Id = dbo.ItemSizes.Item_Id
INNER JOIN CampaignOffers ON CampaignOffers.ItemID = ItemSizes.IS_Id
INNER JOIN dbo.ItemCategories ON dbo.Items.ItemCat_Id = dbo.ItemCategories.ItemCat_Id
INNER JOIN dbo.Providers ON dbo.ProviderItems.Provider_Id = dbo.Providers.Provider_Id
INNER JOIN dbo.Branches ON dbo.Providers.Provider_Id = dbo.Branches.Provider_Id
where Fk_CampaignID=#Fk_CampaignID
group by NEWID(),
CampaignID, CampaignOffers.OldPrice ,
dbo.ItemSizes.IS_Id,
dbo.Items.[Item_Description_L2],
dbo.Items.[Item_Image],
dbo.Items.[Item_Details],
dbo.ItemSizes.IS_Id,
dbo.ItemSizes.[IS_Price] ,
-- if null then it is not a featured meal
dbo.ProviderItems.[PI_Id] ,
dbo.ItemCategories.[ItemCat_Id],
dbo.Providers.Provider_Name_L2,
dbo.Providers.Provider_Menu_Logo
,dbo.Branches.Branch_Id,
dbo.Providers.Provider_Id,CampaignID,CampaignOffers.OldPrice
order by NEWID()
You need to push the SELECT DISTINCT into an inner query (at which point you can also lose the GROUP BY) then do a NEWID() in the outer query. The general form is
select
newid(), X.*
from
(
select distinct <cols>
from <tables>
where <whatever>
) X
order by 1
in your case I think this is what you want:
select
newid(), X.*
from
(
select distinct
CampaignID, CampaignOffers.OldPrice ,
dbo.ItemSizes.IS_Id,
dbo.Items.[Item_Description_L2] as Item_Description ,
dbo.Items.[Item_Image],
dbo.Items.[Item_Details],
dbo.ItemSizes.[IS_Price] as Price,
-- if null then it is not a featured meal
dbo.ProviderItems.[PI_Id] as ProviderItems_PI_ID,
dbo.ItemCategories.[ItemCat_Id],
dbo.Providers.Provider_Name_L2 as Provider_Name,
dbo.Providers.Provider_Menu_Logo,
dbo.Providers.Provider_Id
FROM
Items
INNER JOIN ProviderItems ON Items.Item_Id = ProviderItems.Item_Id
INNER JOIN dbo.ItemSizes ON dbo.Items.Item_Id = dbo.ItemSizes.Item_Id
inner join CampaignOffers ON CampaignOffers.ItemID = ItemSizes.IS_Id
INNER JOIN dbo.ItemCategories ON dbo.Items.ItemCat_Id = dbo.ItemCategories.ItemCat_Id
INNER JOIN dbo.Providers ON dbo.ProviderItems.Provider_Id = dbo.Providers.Provider_Id
INNER JOIN dbo.Branches ON dbo.Providers.Provider_Id = dbo.Branches.Provider_Id
where
Fk_CampaignID = #Fk_CampaignID
) X
order by 1
Remove the NEWID()'s from your query and surround it with
SELECT * FROM (
<your query>
) as t
ORDER BY NEWID()
This query doesn't make much sense, there is no aggregation, so what is the purpose of the group by statement? and grouping by a NEWID() is not going to get you any grouping's anyway.
Combine the superfluous group by with the distinct clause and it feels more like you have a join condition incorrect that is causing a cartesian, and you are desperately trying to use distinct and group by's to eliminate that cartesian, it certainly makes little sense to have them both in the statement, and the newid in the group by should be removed.