how to optimized without union clauses? - sql

Is it possible to write below query without a union clause.
select ProductId,ImageName,ImageType, ROW_NUMBER() over (order by ProductId desc) RowId
from
(
select p.id ProductId ,p.pic_image ImageName,'pic_image' ImageType
from product p
left outer join iimages_edited pe on p.id = pe.[id]
where isnull(p.pic_image,'') <> '' and isnull(pe.pic_image,0)=0
union
select p.id ProductId,p.pic_bimage ImageName,'pic_bimage' ImageType
from product p
left outer join iimages_edited pe on p.id = pe.[id]
where isnull(p.pic_bimage,'') <> '' and isnull(pe.pic_bimage,0)=0
union
select p.id ProductId,p.pic_limage ImageName,'pic_limage' ImageType
from product p
left outer join iimages_edited pe on p.id = pe.[id]
where isnull(p.pic_limage,'') <> '' and isnull(pe.pic_limage,0)=0
union
select p.id ProductId,p.pic_blimage ImageName,'pic_blimage' ImageType
from product p
left outer join iimages_edited pe on p.id = pe.[id]
where isnull(p.pic_blimage,'') <> '' and isnull(pe.pic_blimage,0)=0
union
select p.id ProductId,p.pic_cimage ImageName,'pic_cimage' ImageType
from product p
left outer join iimages_edited pe on p.id = pe.[id]
where isnull(p.pic_cimage,'') <> '' and isnull(pe.pic_cimage,0)=0
)t
Above query has same table but different where condition, It is
possible to do it in a single query ?
Any help will be much appreciated !
Thanks in advance

It seems that you are repeating the same join and filters with differents columns each time. You can convert them to rows using UNPIVOT, on each table, before the join :
select pe.ProductId, p.ProductId, p.ImageName, p.ImageType, ROW_NUMBER()
over (order by p.ProductId desc) RowId
from (
select id as ProductId, ImageType, ImageName
from product
unpivot (
ImageType for ImageName
in (pic_image, pic_bimage, pic_limage, pic_blimage, pic_cimage)
) t
) as p
left outer join (
select id as ProductId, ImageType, ImageName
from iimages_edited
unpivot (
ImageType for ImageName
in (pic_image, pic_bimage, pic_limage, pic_blimage, pic_cimage)
) t
) as pe
on p.ImageType = pe.ImageType
and p.ProductId = pe.ProductId
where pe.ProductId is null
UNPIVOT filters null values, so ISNULL are probably not necessary.

Related

How to get value from unique id basis varchar datatype

I have the following query; can you help me with it? I have comboAmount and TotalPrice, I want TotalPrice + Combo Amount add into newComboAmount, but it's showing me a wrong result because my ComboUniqueId is different from other please check below picture and query
SELECT
CAST(CASE
WHEN a.TotalPrice != 0
THEN 1
ELSE 0
END AS bit) AS CheckOtherPrice,
a.OrderDetailId, a.ItemId,
b.ItemCode, b.ItemName,
e.GroupName,
a.Quantity, a.Price, a.TotalPrice,
a.OrderId, ISNULL(a.ComboId, 0) ComboId,
(a.ComboAmount + SUM(a.TotalPrice) OVER (ORDER BY a.OrderDetailId)) AS new_comboamount,
a.ComboGroupId, a.ComboAmount, a.ComboUniqueId,
c.ComboName, d.GroupName ComboGroupName,
a.OfferMasterId
FROM
OrderDetail a
INNER JOIN
ItemMaster b ON a.ItemId = b.ItemId AND a.OrderId IN (985)
INNER JOIN
ItemGroup e ON b.ItemGroupId = e.ItemGroupId
LEFT JOIN
ComboMaster c ON a.ComboId = c.ComboId
LEFT JOIN
ComboGroup d ON a.ComboGroupId = d.GroupId
I think you just need a partition by:
( a.ComboAmount + sum(a.TotalPrice) over (partition by combouniqueid order by a.OrderDetailId) ) as new_comboamount

SQL query optimization - make only one join on table

I have a large SQL query, where I need to select some data.
SELECT p.Id, p.UserId, u.Name AS CreatedBy, p.JournalId, p.Title, pt.Name AS PublicationType, p.CreatedDate, p.MagazineTitle, /*ps.StatusId,*/ p.Authors, pb.Name AS Publisher, p.Draft,jns.Name AS JournalTitle,
ISNULL(
ISNULL(
(SELECT StatusId FROM [PublicationsStatus] WHERE StatusDate=
(SELECT MAX(StatusDate) FROM [PublicationsStatus] AS ps WHERE ps.PublicationId = p.Id )),--AND ps.UserId = #UserId ORDER BY StatusDate DESC),
(SELECT TOP(1) ActionId + 6 FROM [PublicationsQuoteSaleLines] AS pqsl WHERE pqsl.PublicationId = p.Id ORDER BY pqsl.Id)
),
1
)AS StatusId
,ISNULL(
(SELECT MAX(StatusDate) FROM [PublicationsStatus] AS ps WHERE ps.PublicationId = p.Id ),--AND ps.UserId = #UserId),
p.CreatedDate
) AS StatusDate
,ISNULL(
(cast((SELECT MAX(StatusDate) FROM [PublicationsStatus] AS ps WHERE ps.PublicationId = p.Id) as date) ),--AND ps.UserId = #UserId),
p.CreatedDate
) AS StDate
,CASE
WHEN ISNULL(
ISNULL(
(SELECT StatusId FROM [PublicationsStatus] WHERE StatusDate=
(SELECT MAX(StatusDate) FROM [PublicationsStatus] AS ps WHERE ps.PublicationId = p.Id )),--AND ps.UserId = #UserId ORDER BY StatusDate DESC),
(SELECT TOP(1) ActionId + 6 FROM [PublicationsQuoteSaleLines] AS pqsl WHERE pqsl.PublicationId = p.Id ORDER BY pqsl.Id)
),
1 ) IN (1, 7, 8) THEN 0
ELSE 1 END AS OrderCriteria
,(SELECT COUNT(*) FROM SentEmails AS se WHERE se.PublicationId = p.Id AND se.EmailType = 1 AND se.UserId = #UserId) AS NumberOfAlerts
,(SELECT COUNT(*) FROM SentEmails AS se WHERE se.PublicationId = p.Id AND se.EmailType = 3 AND se.UserId = #UserId) AS NumberOfReminders
FROM Publications AS p
LEFT JOIN PublicationTypes AS pt ON p.PublicationTypeId = pt.Id
LEFT JOIN Publishers AS pb ON p.PublisherId = pb.Id
LEFT JOIN Journals As jns ON p.JournalId = jns.Id
LEFT JOIN Users AS u ON u.Id = p.UserId
The problem is that the query is slow. AS you can see I have the same thing at OrderCriteria and the StatusId. The StatusDate I'm getting from the same table.
I thought that I could resolve the performance to make only one \
LEFT JOIN
something like this:
LEFT JOIN (
SELECT
PublicationId,
StatusId AS StatusId,
StatusDate AS StatusDate
FROM [PublicationsStatus] WHERE StatusDate=
(
SELECT MAX(StatusDate) FROM PublicationsStatus
)
) AS ps ON ps.PublicationId = p.Id
but I did not get the same results this way.
Can you please advise?
I tried to simplify your query using a few CTE to avoid doing the same subquery multiple times. You can try this out and see if it's still slow.
;WITH MaxStatusDateByPublication AS
(
SELECT
PublicationId = ps.PublicationId,
MaxStatusDate = MAX(ps.StatusDate)
FROM
[PublicationsStatus] AS ps
GROUP BY
PS.PublicationId
),
StatusForMaxDateByPublication AS
(
SELECT
StatusId = PS.StatusId,
M.PublicationId,
M.MaxStatusDate
FROM
MaxStatusDateByPublication AS M
INNER JOIN [PublicationsStatus] AS PS ON
M.PublicationId = PS.PublicationId AND
M.MaxStatusDate = PS.StatusDate
),
SentEmailsByPublicationAndType AS
(
SELECT
S.PublicationID,
S.EmailType,
AmountSentEmails = COUNT(1)
FROM
SentEmails AS S
WHERE
S.EmailType IN (1, 3) AND
S.UserID = #UserId
GROUP BY
S.PublicationID,
S.EmailType
)
SELECT
p.Id,
p.UserId,
u.Name AS CreatedBy,
p.JournalId,
p.Title,
pt.Name AS PublicationType,
p.CreatedDate,
p.MagazineTitle,
p.Authors,
pb.Name AS Publisher,
p.Draft,
jns.Name AS JournalTitle,
COALESCE(MS.StatusId, SL.StatusId, 1) AS StatusId,
ISNULL(MS.MaxStatusDate, P.CreatedDate) AS StatusDate,
ISNULL(CONVERT(DATE, MS.MaxStatusDate), P.CreatedDate) AS StDate,
CASE
WHEN COALESCE(MS.StatusId, SL.StatusId, 1) IN (1, 7, 8) THEN 0
ELSE 1
END AS OrderCriteria,
ISNULL(TY1.AmountSentEmails, 0) AS NumberOfAlerts,
ISNULL(TY3.AmountSentEmails, 0) AS NumberOfReminders
FROM
Publications AS p
LEFT JOIN PublicationTypes AS pt ON p.PublicationTypeId = pt.Id
LEFT JOIN Publishers AS pb ON p.PublisherId = pb.Id
LEFT JOIN Journals As jns ON p.JournalId = jns.Id
LEFT JOIN Users AS u ON u.Id = p.UserId
LEFT JOIN StatusForMaxDateByPublication AS MS ON P.Id = MS.PublicationId
LEFT JOIN SentEmailsByPublicationAndType AS TY1 ON
P.Id = TE.PublicationID AND
TY1.EmailType = 1
LEFT JOIN SentEmailsByPublicationAndType AS TY3 ON
P.Id = TE.PublicationID AND
TY1.EmailType = 3
OUTER APPLY (
SELECT TOP 1
StatusId = ActionId + 6
FROM
[PublicationsQuoteSaleLines] AS pqsl
WHERE
pqsl.PublicationId = P.Id
ORDER BY
pqsl.Id ASC) AS SL
Try to avoid writing the same expression several times (and specially if it involes subqueries inside a column!). Using a few CTEs and proper identing will help readability.
This is a complex query and involves several tables. If your query runs slow it might be for many different reasons. Try executing each subquery on it's own to check if they are slow or not, then try joining them 1 by 1. Indexes by the join columns will probably increase your performance if they don't exist already. Posting the full query execution plan might help.

Finding the count

I have the following SQL query and need to know the count of companyid as I can see repeating data. How do I find the count of it. Following is the query
SELECT a.companyId 'companyId'
, i.orgDebtType 'orgDebtType'
, d.ratingTypeName 'ratingTypeName'
, c.currentRatingSymbol 'currentRatingSymbol'
, c.ratingStatusIndicator 'ratingStatusIndicator'
, g.qualifierValue 'qualifierValue'
, c.ratingdate 'ratingDate'
, h.value 'outlook'
FROM ciqRatingEntity a
JOIN ciqcompany com
on com.companyId = a.companyId
JOIN ciqratingobjectdetail b ON a.entitySymbolValue = b.objectSymbolValue
JOIN ciqRatingData c ON b.ratingObjectKey = c.ratingObjectKey
JOIN ciqRatingType d ON b.ratingTypeId = d.ratingTypeId
JOIN ciqRatingOrgDebtType i ON i.orgDebtTypeId=b.orgDebtTypeId
JOIN ciqRatingEntityData red ON red.entitySymbolValue=a.entitySymbolValue
AND red.ratingDataItemId='1' ---CoName
LEFT JOIN ciqRatingDataToQualifier f ON f.ratingDataId = c.ratingDataId
LEFT JOIN ciqRatingQualifiervalueType g ON g.qualifiervalueid = f.qualifierValueId
LEFT JOIN ciqRatingValueType h ON h.ratingValueId = c.outlookValueId
WHERE 1=1
AND b.ratingTypeId IN ( '130', '131', '126', '254' )
-- and a.companyId = #companyId
AND a.companyId IN
(SELECT distinct TOP 2000000
c.companyId
FROM ciqCompany c
inner join ciqCompanyStatusType cst on cst.companystatustypeid = c.companystatustypeid
inner join ciqCompanyType ct on ct.companyTypeId = c.companyTypeId
inner join refReportingTemplateType rep on rep.templateTypeId = c.reportingtemplateTypeId
inner join refCountryGeo rcg on c.countryId = rcg.countryId
inner join refState rs on rs.stateId = c.stateId
inner join ciqSimpleIndustry sc on sc.simpleIndustryId = c.simpleIndustryId
ORDER BY companyid desc)
ORDER BY companyId DESC, c.ratingdate, b.ratingTypeId, c.ratingStatusIndicator
This will list where there are duplicate companyID's
SELECT companyId, count(*) as Recs
FROM ciqCompany
GROUP BY ciqCompany
HAVING count(*) > 1
I understand that you wish to add a column to the query with the count of each companyId, you can use COUNT() OVER():
select count(a.companyId) over (partition by a.companyId) as companyCount,
<rest of the columns>
from ciqRatingEntity a
join <rest of the query>
This would return in each row the count of the companyId of that row without grouping the results.

Too many results in query

I'm fetching some data from our database in MSSQL. Out of this data I want to determine who created the client entry and who took the first payment from this client.
There can be many payment entries for a client on a single booking/enquiry and at the moment, my query shows results for each payment. How can I limit the output to only show the first payment entry?
My query:
SELECT
c.FirstName,
c.LastName,
c.PostalCode,
o.OriginOfEnquiry,
s.SuperOriginName,
c.DateOfCreation,
DATEDIFF(day, c.DateOfCreation, p.DateOfCreation) AS DaysToPayment,
pc.PackageName,
CONCAT(u.FirstName, ' ', u.LastName) AS CreateUser,
(SELECT CONCAT(u.FirstName, ' ', u.LastName)
WHERE u.UserID = p.UserID ) AS PaymentUser
FROM tblBookings b
INNER JOIN tblPayments p
ON b.BookingID = p.BookingID
INNER JOIN tblEnquiries e
ON e.EnquiryID = b.EnquiryID
INNER JOIN tblCustomers c
ON c.CustomerID = e.CustomerID
INNER JOIN tblOrigins o
ON o.OriginID = e.OriginID
INNER JOIN tblSuperOrigins s
ON s.SuperOriginID = o.SuperOriginID
INNER JOIN tblBookingPackages bp
ON bp.bookingID = p.BookingID
INNER JOIN tblPackages pc
ON pc.PackageID = bp.packageID
INNER JOIN tblUsers u
ON u.UserID = c.UserID
WHERE c.DateOfCreation >= '2016-06-01' AND c.DateOfCreation < '2016-06-30'
AND p.PaymentStatusID IN (1,2)
AND e.CustomerID = c.CustomerID
AND p.DeleteMark != 1
AND c.DeleteMark != 1
AND b.DeleteMark != 1
;
I tried adding a "TOP 1" to the nested select statement for PaymentUser, but it made no difference.
you can use cross apply with top 1:
FROM tblBookings b
cross apply
(select top 1 * from tblPayments p where b.BookingID = p.BookingID) as p
Instead of table tblPayments specify sub-query like this:
(SELECT TOP 1 BookingID, UserID, DateOfCreation
FROM tblPayments
WHERE DeleteMark != 1
AND PaymentStatusID IN (1,2)
ORDER BY DateOfCreation) as p
I'm assuming that tblPayments has a primary key column ID. If it is true, you can use this statment:
FROM tblBookings b
INNER JOIN tblPayments p ON p.ID = (
SELECT TOP 1 ID
FROM tblPayments
WHERE BookingID = b.BookingID
AND DeleteMark != 1
AND PaymentStatusID IN (1,2)
ORDER BY DateOfCreation)

Count from another table with join

How can I Count the Lending comments for each lending (the comments is on another table called "LendingComments" with a reference Column called "LendingId" ?
SELECT LendingStatus.Status, Products.Productname, Products.Serial_number, Deposits.Amount, Lendings.DeliveryDate, Lendings.Id AS LendingId, Products.Id AS ProductId FROM Lendings
LEFT JOIN Products ON Lendings.ProductId = Products.Id
LEFT JOIN LendingStatus ON Lendings.StatusId = LendingStatus.Id
LEFT JOIN Deposits ON Lendings.DepositId = Deposits.Id
WHERE PersonId = 561 ORDER BY DeliveryDate DESC
Maby like this (if I understand the question well enough)
SELECT
LendingStatus.Status, Products.Productname, Products.Serial_number,Deposits.Amount, Lendings.DeliveryDate, Lendings.Id AS LendingId, Products.Id AS ProductId, LendingComments.NumLendingComments
FROM Lendings
LEFT JOIN Products ON Lendings.ProductId = Products.Id
LEFT JOIN LendingStatus ON Lendings.StatusId = LendingStatus.Id
LEFT JOIN Deposits ON Lendings.DepositId = Deposits.Id
OUTER APPLY
(
SELECT
COUNT(*) AS NumLendingComments
FROM
LendingComments PL
WHERE
PL.LendingID = Lendings.LendingID
) AS LendingComments WHERE Personid = 561 ORDER BY DeliveryDate desc
Maybe this helps:
SELECT CommentCount = Sum(lc.comments)
OVER (
partition BY lc.id),
lendingstatus.status,
products.productname,
products.serial_number,
deposits.amount,
lendings.deliverydate,
lendings.id AS LendingId,
products.id AS ProductId
FROM lendings
LEFT JOIN products
ON lendings.productid = products.id
LEFT JOIN lendingstatus
ON lendings.statusid = lendingstatus.id
LEFT JOIN deposits
ON lendings.depositid = deposits.id
LEFT JOIN LendingComments lc
ON lc.LendingId = lendings.Lendings.Id
WHERE personid = 561
ORDER BY deliverydate DESC
However, you have not shown the PersonLendings table, have you?
Try this one -
SELECT ls.status
, p.Productname
, p.Serial_number
, d.AMOUNT
, l.DeliveryDate
, l.Id AS LendingId
, p.Id AS ProductId
, pl.cnt
FROM dbo.Lendings l
LEFT JOIN (
SELECT pl.LendingId, cnt = COUNT(pl.LendingComments)
FROM dbo.PersonLendings pl
GROUP BY pl.LendingId
) pl ON pl.LendingId = l.LendingId
LEFT JOIN dbo.Products p ON l.ProductId = p.Id
LEFT JOIN dbo.LendingStatus ls ON l.StatusId = ls.Id
LEFT JOIN dbo.Deposits d ON l.DepositId = d.Id
WHERE PersonID = 561
ORDER BY l.DeliveryDate DESC