I'm using SQL Server 2014. I have a structure like this:
Id BIGINT,
ItemName NVARCHAR(4000),
RecordDate DATETIME2,
Supplier NVARCHAR(450),
Quantity DECIMAL(18, 2),
ItemUnit NVARCHAR(2000),
EntityUnit NVARCHAR(2000),
ItemSize DECIMAL(18, 2),
PackageSize DECIMAL(18, 2),
FamilyCode NVARCHAR(20),
Family NVARCHAR(500),
CategoryCode NVARCHAR(20),
Category NVARCHAR(500),
SubCategoryCode NVARCHAR(20),
SubCategory NVARCHAR(500),
ItemGroupCode NVARCHAR(20),
ItemGroup NVARCHAR(500),
PurchaseValue DECIMAL(18, 2),
UnitPurchaseValue DECIMAL(18, 2),
PackagePurchaseValue DECIMAL(18, 2),
FacilityCode NVARCHAR(450),
CurrencyCode NVARCHAR(5)
I'd like to select distinct ItemNames from BatchRecords table paired with the max Id among the items with the same ItemName as well as Supplier, Quantity and other values of the item with the max Id for each ItemName. So far, I came up with the following SP, definitely it doesn't work yet as GROUP BY throws an error.
I could probably use a subquery, but then how do I satisfy the condition with max Ids for each unique ItemName? Also, any input to the stored procedure quality/obvious bottleneck is highly appreciated as it has to be somewhat quick.
CREATE PROCEDURE dbo.GetRecordsPageFlat
(#BatchIds dbo.GenericIntArray READONLY,
#FileRequestId INT,
#PageSize INT,
#PageCount INT,
#LastId BIGINT,
#NameMaskValue NVARCHAR(128) = NULL,
#NameMaskType INT = NULL,
#FamilyCodeMaskValue NVARCHAR(128),
#CategoryCodeMaskValue NVARCHAR(128),
#SubCategoryCodeMaskValue NVARCHAR(128)
)
AS
SET NOCOUNT ON;
DECLARE #Temp dbo.RecordImportStructure
DECLARE #ErrorCode INT
DECLARE #Step NVARCHAR(200)
DECLARE #Rows INT
--OUTPUT ##ROWCOUNT
--OUTPUT INSERTED.Id
INSERT INTO #Temp (
Id,
ItemName,
Supplier,
Quantity,
ItemUnit,
EntityUnit,
ItemSize,
PackageSize,
PurchaseValue,
UnitPurchaseValue,
PackagePurchaseValue,
CurrencyCode
)
SELECT
BR.Id,
BR.ItemName,
BR.Supplier,
BR.Quantity,
BR.ItemUnit,
BR.EntityUnit,
BR.ItemSize,
BR.PackageSize,
BR.ItemGroup,
BR.UnitPurchaseValue,
BR.PackagePurchaseValue,
C.IsoCode
FROM
dbo.BatchRecords BR
LEFT OUTER JOIN
dbo.FacilityInstances F ON F.Id = BR.FacilityInstanceId
LEFT OUTER JOIN
dbo.Currencies C ON C.Id = BR.CurrencyId
--OPTION(RECOMPILE)
WHERE
BR.DataBatchId IN (SELECT * FROM #BatchIds)
AND BR.Id > #LastId
AND (#FamilyCodeMaskValue IS NULL OR BR.FamilyCode = #FamilyCodeMaskValue)
AND (#CategoryCodeMaskValue IS NULL OR BR.CategoryCode = #CategoryCodeMaskValue)
AND (#SubCategoryCodeMaskValue IS NULL OR BR.SubCategoryCode = #SubCategoryCodeMaskValue)
AND (#NameMaskType IS NULL AND #NameMaskValue IS NULL
OR ((#NameMaskType = 1 AND BR.ItemName LIKE #NameMaskValue + '%')
OR (#NameMaskType = 2 AND BR.ItemName LIKE '%' + #NameMaskValue)
OR (#NameMaskType = 3 AND BR.ItemName LIKE '%' + #NameMaskValue + '%')
))
GROUP BY
BR.ItemName
ORDER BY
BR.Id
OFFSET #PageCount * #PageSize ROWS
FETCH NEXT #PageSize ROWS ONLY;
UPDATE dbo.BatchActionRequests
SET PageNumber = #PageCount+1,
LatestItemId = (SELECT MAX(Id) FROM #Temp)
WHERE Id = #FileRequestId
It looks like a top-n-per-group problem.
There are two common approaches to it: using ROW_NUMBER and CROSS APPLY. Here is the ROW_NUMBER variant. See Get top 1 row of each group for details.
WITH
CTE
AS
(
SELECT
BR.Id,
BR.ItemName,
BR.Supplier,
BR.Quantity,
BR.ItemUnit,
BR.EntityUnit,
BR.ItemSize,
BR.PackageSize,
-- BR.ItemGroup,???
BR.UnitPurchaseValue,
BR.PackagePurchaseValue,
C.IsoCode AS CurrencyCode,
ROW_NUMBER() OVER (PARTITION BY BR.ItemName ORDER BY BR.Id DESC) AS rn
FROM
dbo.BatchRecords BR
LEFT OUTER JOIN dbo.FacilityInstances F ON F.Id = BR.FacilityInstanceId
LEFT OUTER JOIN dbo.Currencies C ON C.Id = BR.CurrencyId
WHERE
BR.DataBatchId IN (SELECT * FROM #BatchIds)
AND BR.Id > #LastId
AND (#FamilyCodeMaskValue IS NULL OR BR.FamilyCode = #FamilyCodeMaskValue)
AND (#CategoryCodeMaskValue IS NULL OR BR.CategoryCode = #CategoryCodeMaskValue)
AND (#SubCategoryCodeMaskValue IS NULL OR BR.SubCategoryCode = #SubCategoryCodeMaskValue)
AND (#NameMaskType IS NULL AND #NameMaskValue IS NULL
OR ((#NameMaskType = 1 AND BR.ItemName LIKE #NameMaskValue + '%')
OR (#NameMaskType = 2 AND BR.ItemName LIKE '%' + #NameMaskValue)
OR (#NameMaskType = 3 AND BR.ItemName LIKE '%' + #NameMaskValue + '%')
))
)
INSERT INTO #Temp (
Id,
ItemName,
Supplier,
Quantity,
ItemUnit,
EntityUnit,
ItemSize,
PackageSize,
-- PurchaseValue,???
UnitPurchaseValue,
PackagePurchaseValue,
CurrencyCode
)
SELECT
Id,
ItemName,
Supplier,
Quantity,
ItemUnit,
EntityUnit,
ItemSize,
PackageSize,
-- PurchaseValue,???
UnitPurchaseValue,
PackagePurchaseValue,
CurrencyCode
FROM CTE
WHERE rn = 1
ORDER BY
Id
OFFSET #PageCount * #PageSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(RECOMPILE);
For each ItemName the query will pick the row with the largest Id.
;WITH CTC
AS
(
SELECT MAX(BR.ID) AS Id, BR.ItemName
FROM dbo.BatchRecords BR
LEFT OUTER JOIN dbo.FacilityInstances F ON F.Id = BR.FacilityInstanceId
WHERE BR.DataBatchId IN (SELECT * FROM #BatchIds)
AND BR.Id > #LastId
AND (#FamilyCodeMaskValue IS NULL OR BR.FamilyCode = #FamilyCodeMaskValue)
AND (#CategoryCodeMaskValue IS NULL OR BR.CategoryCode = #CategoryCodeMaskValue)
AND (#SubCategoryCodeMaskValue IS NULL OR BR.SubCategoryCode = #SubCategoryCodeMaskValue)
AND (#NameMaskType IS NULL AND #NameMaskValue IS NULL
OR ((#NameMaskType = 1 AND BR.ItemName LIKE #NameMaskValue + '%')
OR (#NameMaskType = 2 AND BR.ItemName LIKE '%' + #NameMaskValue)
OR (#NameMaskType = 3 AND BR.ItemName LIKE '%' + #NameMaskValue + '%')
))
GROUP BY
BR.ItemName
)
INSERT INTO #Temp (
Id,
ItemName,
Supplier,
Quantity,
ItemUnit,
EntityUnit,
ItemSize,
PackageSize,
PurchaseValue,
UnitPurchaseValue,
PackagePurchaseValue,
CurrencyCode
)
SELECT BR.Id,
BR.ItemName,
BR.Supplier,
BR.Quantity,
BR.ItemUnit,
BR.EntityUnit,
BR.ItemSize,
BR.PackageSize,
BR.ItemGroup,
BR.UnitPurchaseValue,
BR.PackagePurchaseValue,
C.IsoCode
FROM CTC t
JOIN dbo.BatchRecords BR ON t.Id = BR.Id
LEFT OUTER JOIN dbo.Currencies C ON C.Id = BR.CurrencyId
ORDER BY BR.Id
OFFSET #PageCount * #PageSize ROWS
FETCH NEXT #PageSize ROWS ONLY;
Related
I have procedure in which I am getting data from 4 tables by joining them.
Here is my code
alter PROCEDURE [dbo].[GetServiceRequestforLead]
#professionalArea nvarchar (35) = '',
#status nvarchar (15) = '',
#experience nvarchar (15) = '',
#PageNumber BIGINT = 1,
#PageSize BIGINT =20,
#userid bigint = 0
AS
BEGIN
SELECT *
FROM (
SELECT Row_number() OVER (ORDER BY sc.Addeddate DESC) AS Row, sc.*
, TotalRows = Count(*) OVER()
FROM (
select Mod_UserDetails.Email as LeadEmailId, Mod_UserDetails.ID as LeaduserId
, ProfessionType, Mod_UserDetails.FirstName as LeadFirstName, Mod_UserDetails.LastName as LeadLastName
, Mod_UserDetails.PhoneNo1 as LeadMobile
, f.*
from Lead_RequestForm f
inner join Lead_ProfessionalArea on Lead_ProfessionalArea.ID = f.Profession
left join ServiceProviderRequestMapping on f.RequestId = ServiceProviderRequestMapping.RequestId
left join mod_userdetails on mod_userdetails.ID = ServiceProviderRequestMapping.Leaduserid
where f.ExperienceRequired like '%'+#experience+'%'
AND f.Status like '%' + #status + '%'
AND Lead_ProfessionalArea.ProfessionType like '%'+ #professionalArea +'%'
AND (ServiceProviderRequestMapping.IsDeleted is null OR ServiceProviderRequestMapping.IsDeleted = 0)
) sc
) AS query
WHERE row > ( #PageSize *( #PageNumber -1))
AND row <= ( #PageSize * #PageNumber )
END
By executing this I am getting more than one row for RequestId column of Lead_RequestForm f table. Data is unique in both the rows but reuqest id is the same. I want to get only the first record with the same Requestid column.
i have added snap here request id column has duplicate value i want only one roq with a request id so want to select first one only
You can do as below -
alter PROCEDURE [dbo].[GetServiceRequestforLead]
#professionalArea nvarchar (35) = '',
#status nvarchar (15) = '',
#experience nvarchar (15) = '',
#PageNumber BIGINT = 1,
#PageSize BIGINT =20,
#userid bigint = 0
AS
BEGIN
Select Tb.*
from
(SELECT t.*, row_number() OVER (partition by RequestId order by Rowid) as seq_nbr
FROM (
SELECT Row_number() OVER (ORDER BY sc.Addeddate DESC) AS Row, sc.*
, TotalRows = Count(*) OVER()
FROM (
select Mod_UserDetails.Email as LeadEmailId, Mod_UserDetails.ID as LeaduserId
, ProfessionType, Mod_UserDetails.FirstName as LeadFirstName, Mod_UserDetails.LastName as LeadLastName
, Mod_UserDetails.PhoneNo1 as LeadMobile
, f.*
from Lead_RequestForm f
inner join Lead_ProfessionalArea on Lead_ProfessionalArea.ID = f.Profession
left join ServiceProviderRequestMapping on f.RequestId = ServiceProviderRequestMapping.RequestId
left join mod_userdetails on mod_userdetails.ID = ServiceProviderRequestMapping.Leaduserid
where f.ExperienceRequired like '%'+#experience+'%'
AND f.Status like '%' + #status + '%'
AND Lead_ProfessionalArea.ProfessionType like '%'+ #professionalArea +'%'
AND (ServiceProviderRequestMapping.IsDeleted is null OR ServiceProviderRequestMapping.IsDeleted = 0)
) sc
) AS query
WHERE row > ( #PageSize *( #PageNumber -1))
AND row <= ( #PageSize * #PageNumber )
)t
)Tb
where Tb.seq_nbr = 1
END
I got a simple query which return a results from an OrderLine table. Is there a way to visually separate the query results to make it easier to read, like in the image shown here?
SELECT [OrderNo], [LineNo]
FROM [OrderLine]
Results:
drop table if exists #OrderLine;
select object_id as OrderNo, abs(checksum(newid())) as [LineNo]
into #OrderLine
from sys.columns;
-- ... results to text (ctrl+T)?
select OrderNo, [LineNo],
case when lead(OrderNo, 1) over(partition by OrderNo order by OrderNo) = OrderNo then '' else replicate('-', 11) + char(10) end
from #OrderLine;
--inject NULL
select case when [LineNo] is null and flag=2 then null else TheOrderNo end as OrderNo, [LineNo]
from
(
select OrderNo AS TheOrderNo, [LineNo], 1 as flag
from #OrderLine
union all
select distinct OrderNo, NULL, 2
from #OrderLine
) as src
order by TheOrderNo, flag;
You could execute multiple queries like so:
DECLARE #i int = 1
DECLARE #OrderNo
DECLARE #OrderNos TABLE (
Idx smallint Primary Key IDENTITY(1,1)
, OrderNo int
)
INSERT #OrderNos
SELECT distinct [OrderNo] FROM [OrderLine]
WHILE (#i <= (SELECT MAX(idx) FROM #employee_table))
BEGIN
SET #OrderNo = (SELECT [OrderNo] FROM [OrderNos] WHERE [Idx] = #i)
SELECT [OrderNo], [LineNo]
FROM [OrderLine]
WHERE [OrderNo] = #OrderNo
SET #i = #i + 1
END
You can not directly do that. Perhaps add a new column that is either null or has a value in it so that it is alternating.
Something like this:
select ..., case when rank() over (order by OrderNO) % 2 then 'XXX' else null end
from....
The % 2 is a 'modulo' operator...
I have one SP for mobile application chat app (used with API) it has 25 filters and 10 tables join and return posts for user, it works good for 2 days but after 2 days it stop working, then i need to refresh it again with alter query or something then it start working for next few days.
If filter stop for any particular user than why others have issue with SP.
USE [DatabaseName]GOSET ANSI_NULLS ONGOSET QUOTED_IDENTIFIER ONGOALTER PROC [dbo].[uspGetShoutouts] (
#UserId UNIQUEIDENTIFIER,
#anotherUserid UNIQUEIDENTIFIER,
#Interest NVARCHAR(100),
#Gender NVARCHAR(100),
#AgeFrom INT,
#AgeTo INT,
#StartLatitude NUMERIC(20,15),
#StartLongitude NUMERIC(20,15),
#Radius FLOAT,
#Location BIT = NULL,
#IsTraveler BIT,
#IsLocal BIT,
#HomeTownCity VARCHAR(100),
#UserName NVARCHAR(200),
#FirstName NVARCHAR(200),
#LastName NVARCHAR(200),
#CreatedDate DATETIME,
#IsMyShoutout BIT,
#ShoutoutTypeId INT=0,
#PostTypeId INT=0,
#oldMoment BIT,
#SortExpression CHAR(1) = 1,
#CurrentPage INT = 1,
#PageSize INT = 10,
#IsFriend BIT=0,
#IsPrivate BIT,
#GroupId NVARCHAR(200),
#TotalRows INT OUTPUT
)
AS
BEGIN
DECLARE #UserList TABLE(
UserId uniqueidentifier,
FirstName NVARCHAR(100),
LastName NVARCHAR(100),
QuickBloxId NVARCHAR(100),
SocialId NVARCHAR(100),
Gender char(1),
Email NVARCHAR(100),
IsProfileImageSync bit,
IsFriend bit,
IsRequestSent bit,
RequestSender NVARCHAR(100),
IsBlocked bit,
FriendBlocked bit,
Age int,
HomeTownCity NVARCHAR(100),
ImageName NVARCHAR(200),
Distance numeric(20,6),
DistanceTemp numeric(20,6),
LastActiveDateTime datetime,
ShoutoutGuid uniqueidentifier,
ShoutoutId bigint not null,
InterestId int,
InterestName NVARCHAR(100),
ShoutoutImageName NVARCHAR(200),
Description NVARCHAR(max),
CreatedOn DateTime,
IsImageSync bit,
DisplayUpdatedOn NVARCHAR(50),
ShoutoutCity NVARCHAR(500),
ShoutoutCountry NVARCHAR(500),
AdministrativeAreaLevel1 NVARCHAR(500),
PlaceId NVARCHAR(500),
FormattedAaddress NVARCHAR(500),
Url NVARCHAR(500),
TotalLike int,
TotalComment int,
Selflike bit,
ShoutoutTypeId int,
ShoutoutType NVARCHAR(200),
PostTypeId int,
PostTypeName NVARCHAR(200),
oldMoment bit,
UserLatitude numeric(20,6),
UserLongitude numeric(20,6),
ShoutoutLatitude numeric(20,6),
ShoutoutLongitude numeric(20,6),
DistanceShoutoutMiles numeric(20,6)
)
SET NOCOUNT ON;
DECLARE #StartLatitude1 numeric(20,2) = #StartLatitude
DECLARE #StartLongitude1 numeric(20,2) =#StartLongitude;
;with
UserList as (
select
distinct
USRSHTOUT.ShoutoutId,
USRSHTOUT.ShoutoutGuid,
U.UserId,
U.FirstName,
U.LastName,
U.QuickbloxId,
convert(int,round(datediff(hour,dob,getdate())/8766.0,0)) as Age,
U.SocialId,
U.Gender,
U.Email,
isnull(U.IsProfileImageSync,0) IsProfileImageSync,
U.HomeTownCity,
U.ImageName,
isnull(fr.IsFriend,0) as IsFriend,
isnull(fr.IsRequestSent,0) as IsRequestSent,
isnull(fr.RequestSender,cast(cast(0 as binary) as uniqueidentifier)) as RequestSender,
isnull(fr.IsBlocked,0) as IsBlocked,
isnull(fr.FriendBlocked,0) as FriendBlocked,
[dbo].[fnCalcDistanceKM](cast(#StartLatitude1 as float),cast(#StartLongitude1 as float),cast (isnull(USRSHTOUT.Latitude,0) as float) ,cast(isnull(USRSHTOUT.Longitude,0) as float)) as Distance,
(geography::Point(coalesce(USRSHTOUT.Latitude,0), coalesce(USRSHTOUT.Longitude,0), 4326).STDistance(geography::Point(#StartLatitude1, #StartLongitude1, 4326))/1000) as DistanceTemp,
ULAL.UpdatedOn as LastActiveDateTime,
0 as InterestId,
'' as InterestName,
isnull(USRSHTOUT.ImageName,'') 'ShoutoutImageName',
isnull(USRSHTOUT.Description,'') Description,USRSHTOUT.CreatedOn,IsImageSync,
isnull(format(UGL.UpdatedOn,'dd-MMM, yyyy HH:mm tt'),'') 'DisplayUpdatedOn',
isnull(ADC.locality,'')[ShoutoutCity],
isnull(ADC.country,'')[ShoutoutCountry],
isnull(ADC.administrative_area_level_1,'') [AdministrativeAreaLevel1],
isnull(ADC.place_id,'') [PlaceId],
isnull(ADC.formatted_address,'') FormattedAaddress,
isnull(ADC.url,'') Url,
isnull(lt.TotalLike, 0) as TotalLike,
isnull(ct.TotalComment, 0) as TotalComment,
isnull(lt.SelfLike, 0) as SelfLike,
SHT.ShoutoutTypeId,
isnull(SHT.Name,'') ShoutoutType,
UPT.PostTypeId,
isnull(UPT.Name,'') PostTypeName,
USRSHTOUT.oldMoment,
UGL.Latitude 'UserLatitude',
UGL.Longitude 'UserLongitude',
USRSHTOUT.Latitude 'ShoutoutLatitude',
USRSHTOUT.Longitude 'ShoutoutLongitude',
DistanceShoutoutMiles
from
Users U
inner join UserShoutouts USRSHTOUT on U.UserId=USRSHTOUT.UserId
left join UserLastActivityLog ULAL on ULAL.UserId=U.UserId
left join UserGeoLocation UGL on UGL.UserId=U.UserId
left join AddressShoutoutMapping ASMP on ASMP.ShoutoutId=USRSHTOUT.ShoutoutId
left join AddressComponents ADC on ADC.AddressComponentId=ASMP.AddressComponentId
left join [ShoutoutType] SHT on SHT.ShoutoutTypeId=isnull(USRSHTOUT.ShoutoutTypeId,0)
LEFT JOIN [UserPostType] UPT ON UPT.PostTypeId = ISNULL(USRSHTOUT.PostTypeId,0)
cross apply (select cos(radians(#StartLatitude1)) * cos(radians(USRSHTOUT.Latitude)) * cos(radians(USRSHTOUT.Longitude) - radians(#StartLongitude1)) + sin(radians(#StartLatitude1)) * sin(radians(USRSHTOUT.Latitude))) T(ACosInput)
cross apply (select ((3959 * acos(case when abs(ACosInput) > 1 then sign(ACosInput)*1 else ACosInput end)))) T2(DistanceShoutoutMiles)
left join (
select ItemId,
count(1) TotalLike,
cast(sum(case when UserId = #anotherUserid then 1 else 0 end) as bit) as SelfLike
from dbo.LikeDetails
where Liked=1 and LikeSourceId=1
group by ItemId) as lt on USRSHTOUT.ShoutoutId = lt.ItemId
left join (
select ParentId,
count(1) as TotalComment
from Comments
where CommentSourceId=1 and Comments.IsDeleted=0
group by ParentId) as ct on USRSHTOUT.ShoutoutId = ct.ParentId
left join dbo.tvf_GetFriendsByUserId (#UserId) fr on u.UserId = fr.ThisFriendId
left join UserShoutoutsPrivate USP on USP.ShoutoutId = USRSHTOUT.ShoutoutId
left join UserInterest UI on UI.InterestId = USP.GroupId
left join Interest INTRST on INTRST.Id = UI.InterestId
where
USRSHTOUT.IsDeleted=0
and (((#oldMoment = 0) and USRSHTOUT.oldMoment = #oldMoment)
or ((#oldMoment = 1) and USRSHTOUT.oldMoment = 1 or USRSHTOUT.oldMoment = 0) )
and U.IsEmailVerified=1 and isnull(U.IsDeactivated,0)=0
and (#ShoutoutTypeId=0 or USRSHTOUT.ShoutoutTypeId=#ShoutoutTypeId)
and (#IsMyShoutout=0 or (#IsMyShoutout=1 and USRSHTOUT.UserId = #UserId))
and ((#UserName is not null and #UserName='all'
OR ((U.FirstName + ' ' + U.LastName) = #UserName)OR (U.FirstName like LTRIM(RTRIM(#UserName))+'%') OR (U.LastName like LTRIM(RTRIM(#UserName))+'%'))
OR ( #FirstName is not null AND #LastName is null and
((U.FirstName = #FirstName ) OR (U.FirstName like LTRIM(RTRIM(#FirstName))+'%')))
OR ( #LastName is not null AND #FirstName is null and
((U.LastName = #LastName ) OR (U.LastName like LTRIM(RTRIM(#LastName))+'%')))
OR ((( #FirstName is not null AND #LastName is not null AND
((U.FirstName + ' ' + U.LastName) = #FirstName + ' ' + #LastName) OR (U.FirstName like LTRIM(RTRIM(#FirstName))+'%')) and (U.LastName like LTRIM(RTRIM(#LastName))+'%'))))
and((#Interest <>'' AND #IsPrivate = 1 AND (exists (select * from dbo.UserInterest where (UserId = U.UserId and InterestId in (select * from dbo.Split(#Interest,','))))
or #Interest ='')
or(#IsPrivate = 1
and (UI.Interestid in (select items from dbo.Split(#GroupId,',')) and INTRST.IsPrivate = 1 and INTRST.IsDelete = 0 and USRSHTOUT.IsDeleted=0 and
USP.ShoutoutId = USRSHTOUT.ShoutoutId and UI.IsActive = 1 and USP.GroupId in (select items from dbo.Split(#GroupId,','))))
)
OR(#Interest <>'' AND #IsPrivate = 0 AND (exists (select * from dbo.UserInterest where (UserId = U.UserId and InterestId in (select * from dbo.Split(#Interest,','))))
or #Interest ='')
or(#IsPrivate = 1
and (UI.Interestid in (select items from dbo.Split(#GroupId,',')) and INTRST.IsPrivate = 1 and INTRST.IsDelete = 0 and USRSHTOUT.IsDeleted=0 and
USP.ShoutoutId = USRSHTOUT.ShoutoutId and UI.IsActive = 1 and USP.GroupId in (select items from dbo.Split(#GroupId,','))))
)
OR(#Interest ='' AND #IsPrivate = 0 AND (exists (select * from dbo.UserInterest where (UserId = U.UserId and InterestId in (select * from dbo.Split(#Interest,','))))
or #Interest =''))
)
and (#PostTypeId=0 or USRSHTOUT.PostTypeId=#PostTypeId)
)
insert into #UserList
select
UserId,
FirstName,
LastName,
QuickBloxId,
SocialId,
Gender,
Email,
IsProfileImageSync,
IsFriend,
IsRequestSent,
RequestSender,
IsBlocked,
FriendBlocked,
Age,
HomeTownCity,
ImageName,
Distance,
DistanceTemp,
LastActiveDateTime,
ShoutoutGuid,
ShoutoutId,
InterestId,
InterestName,
ShoutoutImageName,
Description,
CreatedOn,
IsImageSync,
DisplayUpdatedOn,
ShoutoutCity,
ShoutoutCountry,
AdministrativeAreaLevel1,
PlaceId,
FormattedAaddress,
Url,
u.TotalLike,
u.TotalComment,
u.Selflike,
ShoutoutTypeId,
ShoutoutType,
PostTypeId,
PostTypeName,
oldMoment,
UserLatitude,
UserLongitude,
ShoutoutLatitude,
ShoutoutLongitude,
DistanceShoutoutMiles
from UserList u
where
(#Gender ='' or ((Gender in(select * from dbo.Split(#Gender,','))) or (#Gender like '%3%' and Gender=0)))
and(
((#AgeFrom = 18) AND ((#AgeTo= 65 and Age >= 15 )
or (Age >= 15 and Age <= #AgeTo)))
OR
((#AgeTo= 65 and Age >= #AgeFrom )
or (Age >= #AgeFrom and Age <= #AgeTo))
)
and IsBlocked <> 1
and FriendBlocked <> 1
and (#Location=0 or ((DistanceShoutoutMiles<= #Radius) or (#IsLocal=1 and HomeTownCity=#HomeTownCity)))
and (#IsFriend=0 or (#IsFriend=1 and IsFriend=1))
and (#Location=0 or (
((#IsTraveler=1 and #IsLocal=1) or (#IsTraveler=0 and #IsLocal=0)) or
((#IsTraveler=1 and #IsLocal=0 and lower(HomeTownCity) != lower(#HomeTownCity))
or (#IsTraveler=0 and #IsLocal=1 and lower(HomeTownCity) = lower(#HomeTownCity)))))OPTION(RECOMPILE)
set #TotalRows = ##rowcount
IF(#CurrentPage=0)
begin
select * from #UserList order by Distance asc OPTION(OPTIMIZE FOR UNKNOWN)
end
ELSE
BEGIN
declare #OffSetSize as bigint
set #OffSetSize = ((#CurrentPage * #PageSize) - #PageSize)
IF (#SortExpression = '1')
BEGIN
SELECT *
FROM #UserList
WHERE CreatedOn<#CreatedDate
ORDER BY CreatedOn DESC
OFFSET #OffSetSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(OPTIMIZE FOR UNKNOWN)
END
ELSE IF (#SortExpression = '2')
BEGIN
SELECT *
FROM #UserList
WHERE CreatedOn>#CreatedDate
ORDER BY CreatedOn DESC
OFFSET #OffSetSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(OPTIMIZE FOR UNKNOWN)
END
ELSE IF (#SortExpression = '3')
BEGIN
SELECT *
FROM #UserList
ORDER BY Distance ASC, CreatedOn DESC
OFFSET #OffSetSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(OPTIMIZE FOR UNKNOWN)
END
ELSE IF (#SortExpression = '4')
BEGIN
SELECT *
FROM #UserList
ORDER BY Distance ASC
OFFSET #OffSetSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(OPTIMIZE FOR UNKNOWN)
END
ELSE
BEGIN
SELECT *
FROM #UserList
ORDER BY CreatedOn DESC
OFFSET #OffSetSize ROWS
FETCH NEXT #PageSize ROWS ONLY
OPTION(OPTIMIZE FOR UNKNOWN)
END
END
ENDGO
I have a customers table and an orders table. I want to display the customer and all of his/her order dates on one row, rather than multiple rows. Here is what I have and what I'm looking for:
Basic code to get results:
select customerid, name, orderdate
from customer_table c inner join
order_table o
on c.customerid = o.customerid
this will work at the most you cant show it on different columns having nulls:
select customer_id,name,LISTAGG(orderdate, ', ') WITHIN GROUP (ORDER BY orderdate)
from(select customerid, name, orderdate
from customer_table c inner join
order_table o
on c.customerid = o.customerid );
You can use the following because you're bound by the 12 order limit. If you expand to an unknown number of orders with no upper limit, then you would need to use dynamic SQL and even then it would be tricky, because you would also need to dynamically create unique column names.
This query batches by customer and sets the order values. They will be in dated order and set as NULL if there's no more orders. It kinda assumes that at least one customer has 12 orders. You'll get a column of all NULLS if that's not the case
IF OBJECT_ID('tempdb..#Results') IS NOT NULL DROP TABLE #Results
IF OBJECT_ID('tempdb..#sortedRows') IS NOT NULL DROP TABLE #SortedRows
DECLARE #CustomerList TABLE(CustomerID INT, RowNo INT);
INSERT INTO #CustomerList SELECT DISTINCT CustomerID, ROW_NUMBER() OVER(ORDER BY CustomerID) RowNo FROM Customer_Table (NOLOCK)
DECLARE #Count INT = (SELECT COUNT(DISTINCT CustomerID) RowNumber FROM #CustomerList)
DECLARE #Counter INT = 0
DECLARE #CustToProcess INT
CREATE TABLE #Results(CustomerID INT, [Name] VARCHAR(50), OrderDate1 DATETIME,
OrderDate2 DATETIME, OrderDate3 DATETIME, OrderDate4 DATETIME, OrderDate5 DATETIME,
OrderDate6 DATETIME, OrderDate7 DATETIME, OrderDate8 DATETIME, OrderDate9 DATETIME,
OrderDate10 DATETIME, OrderDate11 DATETIME, OrderDate12 DATETIME)
INSERT INTO #Results(CustomerID, Name) SELECT DISTINCT CustomerID, Name FROM Customer_Table
SELECT ROW_NUMBER() OVER(PARTITION BY c.CustomerID ORDER BY OrderDate) RowNo,
c.CustomerID, c.Name, t.OrderDate INTO #SortedRows
FROM Customer_Table c (NOLOCK) JOIN Order_Table t ON c.CustomerID = t.CustomerID
WHILE #Counter < #Count
BEGIN
SET #Counter += 1
SET #CustToProcess = (SELECT CustomerID FROM #CustomerList WHERE RowNo = #Counter)
PRINT #CustToProcess
SELECT * INTO #RowsForProcessing FROM #SortedRows WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate1 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 1) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate2 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 2) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate3 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 3) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate4 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 4) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate5 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 5) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate6 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 6) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate7 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 7) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate8 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 8) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate9 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 9) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate10 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 10) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate11 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 11) WHERE CustomerID = #CustToProcess
UPDATE #Results SET OrderDate12 = (SELECT OrderDate FROM #RowsForProcessing WHERE Rowno = 12) WHERE CustomerID = #CustToProcess
DROP Table #RowsForProcessing
END
SELECT * FROM #Results
Try this if you use MS SQL Server:
-- 1st, get the number of columns
declare #columnnumber int = 1
select #columnnumber = max(a.count)
from (
select c.cid,c.name, count(o.orderdate) as count
from
customer_table c
join order_table o
on c.cid = o.cid
group by c.cid,c.name)a
print #columnnumber
-- Compose the column names for Pivot
declare #columnname varchar(max) = ''
declare #int int = 1
while #int <= #columnnumber
begin
set #columnname = #columnname + '[date' + cast(#int as varchar(10))+ '],'
set #int = #int + 1
end
set #columnname = '('+left(#columnname,len(#columnname)-1)+')'
print #columnname
--Pivot !!! + Dynamic SQL
declare #str varchar(max)
set #str =
'SELECT *
FROM
(SELECT c.cid,c.name, o.orderdate,concat(''date'',row_number() over (partition by c.cid,c.name order by o.orderdate)) rnk
FROM customer_table c
join order_table o
on c.cid = o.cid) AS s
PIVOT
(
min(s.orderdate)
FOR s.rnk IN '+ #columnname+
' ) AS PivotTable'
print #str
execute (#str)
Please change the column name. I used cid as your customerid.
Output:
cid name date1 date2 date3
12 John 2017-03-04 2017-05-26 2017-12-01
4 Nancy 2017-02-01 NULL NULL
Like others said, it's in principle impossible (in pure SQL) to generate this not knowing how many orders you have for a single customer.
I like #nikhil-sugandh's answer, it works great if you are OK with having all orders comma-separated in a single column.
If you insist on having multiple columns, you can build on that answer, by replacing LISTAGG with ARRAY_AGG and postprocessing it. It will be MUCH more efficient than e.g. the proposed solution with multiple joins. You can also use ARRAY_SLICE to handle cases when there are more orders than you are prepared for.
Example (note, I added an extra order to demonstrate handling more-than-expected orders
create or replace table customer_table(customerId int, name varchar)
as select * from values
(12,'John'),(4,'Nancy');
create or replace table order_table(orderId int, customerId int, orderDate date)
as select * from values
(1,12,'3/4/2017'),(2,12,'5/26/2017'),(3,12,'12/1/2017'),(4,4,'2/1/2017'),(5,12,'1/1/2019');
with subq as (
select c.customerid, name,
array_agg(orderdate) within group (order by orderdate) as orders
from customer_table c
inner join order_table o on c.customerid = o.customerid
group by c.customerid, c.name
)
select customerid, name,
orders[0]::date AS order1, orders[1]::date AS order2,
array_to_string(array_slice(orders, 2, 999), ' , ') AS overflow
from subq;
------------+-------+------------+------------+-------------------------+
CUSTOMERID | NAME | ORDER1 | ORDER2 | OVERFLOW |
------------+-------+------------+------------+-------------------------+
4 | Nancy | 2017-02-01 | [NULL] | |
12 | John | 2017-03-04 | 2017-05-26 | 2017-12-01 , 2019-01-01 |
------------+-------+------------+------------+-------------------------+
first create a view like this:
create view order_view as
select
count(*) over (partition by customerId order by orderDate) as ord,
CustomerId,
orderdate
from order_table
then you can use this query:
select c.customerid,
o1.orderdate,
o2.orderdate
o3.orderdate
.
.
.
o12.orderdate
from customer_table c
left join order_view o1
on c.customerid = o1.customerid and ord = 1
left join order_view o2
on c.customerid = o2.customerid and ord = 2
left join order_view o3
on c.customerid = o3.customerid and ord = 3
.
.
.
left join order_view o12
on c.customerid = o12.customerid and ord = 12
Using following query i'm going to filter results based on selected tags or categories:
DECLARE #categories NVARCHAR(MAX),
#tags NVARCHAR(MAX);
SELECT #categories = '1,2,4', -- comma separated category ids
#tags = '2,3' -- comma separated tag ids
SELECT p.id,
p.title,
p.price
FROM tbl_products p
LEFT JOIN tbl_product_categories pc ON #categories IS NOT NULL AND pc.product_FK = p.id
LEFT JOIN tbl_product_tags pt ON #tags IS NOT NULL AND pt.product_FK = p.id
WHERE ( p.price >= #min_price OR #min_price IS NULL )
AND ( p.price <= #max_price OR #max_price IS NULL )
AND ( pc.category_FK IN (SELECT value FROM STRING_SPLIT(#categories, ',')) OR #categories IS NULL )
AND ( pt.tag_FK IN (SELECT value FROM STRING_SPLIT(#tags, ',')) OR #tags IS NULL)
GROUP BY p.id
HAVING COUNT(p.id) = ( (SELECT COUNT(*) FROM STRING_SPLIT(#categories, ',')) + (SELECT COUNT(*) FROM STRING_SPLIT(#tags, ',')) )
But it does not produce expected results! I am suspicious that HAVING part does not employed correctly as it does not produce right count every time based on passed tags and category ids.
Does anyone know how we could implement such situations, apply relational division to extract products which have all these passed #categories and #tags in common??? Any better way?
-- update:
for example use the following sample date:
tbl_products:
id title price
===================
1 mouse 10
2 keyboard 18
3 iphone 8 100
4 note 8 90
tbl_product_categories:
product_FK category_FK
======================
1 1
2 1
3 2
4 2
tbl_product_tags:
product_FK tag_FK
=================
1 1
3 1
3 2
4 2
so if we pass #categories = '2' and #tags = '1,2' and min_price = 50 then we should get an iphone 8
Instead of trying to add counts from your variables, you can use count(distinct [tags|categories]) equals the count for the respective parameter like so:
declare #categories nvarchar(max), #tags nvarchar(max), #min_price int, #max_price int;
select
#categories = '2' -- comma separated category ids
, #tags = '1,2' -- comma separated tag ids
, #min_price = 0
, #max_price = power(2,30)
select
p.id
, p.title
, p.price
from tbl_products p
left join tbl_product_categories pc
on #categories is not null and pc.product_fk = p.id
left join tbl_product_tags pt
on #tags is not null and pt.product_fk = p.id
where ( p.price >= #min_price or #min_price is null )
and ( p.price <= #max_price or #max_price is null )
and ( pc.category_fk in (select value from string_split(#categories, ',')) or #categories is null )
and ( pt.tag_fk in (select value from string_split(#tags, ',')) or #tags is null)
group by p.id, p.title, p.price
having (count(distinct pc.category_fk) = (select count(*) from string_split(#categories, ',')) or #categories is null)
and (count(distinct pt.tag_fk) = (select count(*) from string_split(#tags, ',')) or #tags is null)
demo: dbfiddle.uk demo
returns:
+----+----------+-------+
| id | title | price |
+----+----------+-------+
| 3 | iphone 8 | 100 |
+----+----------+-------+
When it comes to performance, you will benefit from rewriting this as a procedure with dynamic sql execution, or at least option (recompile) as shown in these references:
An Updated "Kitchen Sink" Example - Aaron Bertand
Parameter Sniffing, Embedding, and the RECOMPILE Options - Paul White
Dynamic Search Conditions - Erland Sommarskog
Catch-all queries - Gail Shaw
Here is an example of a dynamic sql search procedure for your query that uses exists ...having count()... instead of left join... where... having count(distinct ...) that simplifies the plan a bit (plan comparison demo):
create procedure product_search (
#categories nvarchar(max)
, #tags nvarchar(max)
, #min_price int
, #max_price int
) as
begin;
set nocount, xact_abort on;
declare #sql nvarchar(max);
declare #params nvarchar(256);
set #params = '#categories nvarchar(max), #tags nvarchar(max), #min_price int, #max_price int';
set #sql = ';
select
p.id
, p.title
, p.price
from tbl_products p
where 1=1'
if #min_price is not null
set #sql = #sql + '
and p.price >= #min_price';
if #max_price is not null
set #sql = #sql + '
and p.price <= #max_price';
if #categories is not null
set #sql = #sql + '
and exists (
select 1
from tbl_product_categories ic
where ic.product_fk = p.id
and ic.category_fk in (select value from string_split(#categories, '',''))
having count(*) = (select count(*) from string_split(#categories, '',''))
)';
if #tags is not null
set #sql = #sql + '
and exists (
select 1
from tbl_product_tags it
where it.product_fk = p.id
and it.tag_fk in (select value from string_split(#tags, '',''))
having count(*) = (select count(*) from string_split(#tags, '',''))
)';
exec sp_executesql #sql, #params, #categories, #tags, #min_price, #max_price;
end;
executed like so:
declare #categories nvarchar(max), #tags nvarchar(max), #min_price int, #max_price int;
select
#categories = null -- comma separated category ids
, #tags = '1,2' -- comma separated tag ids
, #min_price = null
, #max_price = power(2,30)
exec product_search #categories, #tags, #min_price, #max_price
demo: dbfiddle.uk demo
returns:
+----+----------+-------+
| id | title | price |
+----+----------+-------+
| 3 | iphone 8 | 100 |
+----+----------+-------+
From your sample data I think you're joining on the wrong column tag_FK instead of product_FK, so the LEFT JOIN on the table tbl_product_tags should be:
LEFT JOIN tbl_product_tags pt ON #tags IS NOT NULL AND pt.product_FK = p.id
Also, I don't think there is a need to use HAVING statement in your query, it seems to me that you're using it as an extra check; since your query is already doing the filtering job. However, the condition after HAVING statement is not correct, and the best example to prove that is your example itself:
1. count of p.Id = 1 (p.Id = 3 ... iPhone 8)
2. count of categories = 1 (category: 2)
3. count of tags = 2 (tags: 1, 2)
then in this case the count of p.Id is not equal to the count of the passed categories and tags.
UPDATE
: based on #dtNiro the query should be as follows:
DECLARE #categories NVARCHAR(MAX),
#tags NVARCHAR(MAX);
SELECT #categories = '1,2,4', -- comma separated category ids
#tags = '2,3' -- comma separated tag ids
SELECT p.id,
p.title,
p.price
FROM tbl_products p
LEFT JOIN tbl_product_categories pc ON #categories IS NOT NULL AND pc.product_FK = p.id
LEFT JOIN tbl_product_tags pt ON #tags IS NOT NULL AND pt.product_FK = p.id
WHERE ( p.price >= #min_price OR #min_price IS NULL )
AND ( p.price <= #max_price OR #max_price IS NULL )
AND ( pc.category_FK IN (SELECT value FROM STRING_SPLIT(#categories, ',')) OR #categories IS NULL )
AND ( pt.tag_FK IN (SELECT value FROM STRING_SPLIT(#tags, ',')) OR #tags IS NULL)
GROUP BY p.id
HAVING (#tags IS NULL OR (COUNT(p.id) = (SELECT COUNT(*) FROM STRING_SPLIT(#tags, ','))))