how to show repeated date value as null in SQL Server? - sql

I have a SQL Query
SELECT DISTINCT DENSE_RANK()
OVER(ORDER BY bsContract.ContractNumber DESC) AS RANKS,
ROW_NUMBER()
OVER(PARTITION BY bsContract.ContractNumber ORDER BY bsContract.ContractNumber)
AS RowsNumber,
dbo.bsContract.ContractNumber,
Advertiser.OrganizationName AS Advertiser,
JobName AS Campaign,
bsContract.FromDate AS StartDate,
Advertiser.OrganizationName AS Client,
(ISNULL(bsContract.FirstName, '') + ' ' +
bsContract.LastName) AS ClientRep,
Advertiser.OrganizationName AS Vendor,
(ISNULL(bsContract.FirstName, '') + ' ' +
bsContract.LastName) AS 'VendorRep',
DocumentType,
FileType
INTO #temp
FROM dbo.bsContract
INNER JOIN Organization AS Advertiser
ON Advertiser.OrganizationID = bsContract.OrganizationID
WHERE FileType IS NULL
AND bsContract.ContractNumber = 'CR6520'
AND bsDocument.SellerID = 3632
SELECT DISTINCT Ranks,
(CASE
WHEN RowsNumber = 1 THEN
ContractNumber
ELSE
''
END) AS ContractNumber,
(CASE
WHEN RowsNumber = 1 THEN
Advertiser
ELSE
''
END) AS Advertiser,
(CASE
WHEN RowsNumber = 1 THEN
Campaign
ELSE
''
END) AS Campaign,
(CASE
WHEN RowsNumber = 1 THEN
StartDate
ELSE
(select convert(datetime, NULL))
END) AS StartDate,
Vendor,
VendorRep
FROM #temp
ORDER BY ContractNumber DESC
Everything is fine it gives the result. The first row gives the value
of Contract Number, Advertiser Campaign, StartDate, Vendor and Vendor
Rep. From 2nd row the value of ContractNumber, Advertiser, Campaign,
StartDate is same so it is shown as Blank. But the first row of the
result is displayed twice due to the Different StartDate. Refer attached screenshot:
How can I solve this issue?

Related

Optimizing a query that uses a lot of aggregates

Heys guys, I asked yesterday about optimizing a query, and with some help I managed to get my query from taking 20 seconds to running instantly.
This was the question (and it contains an example of the underlying table) -> Is there any way to improve the efficiency of this SQL query and make it run faster?
The problem was solved by being smarter about aggregates and group by. However, I still have some trouble with group by. I want to post another query that is against the exact same table and see if anyone sees anyway to improve the efficiency of this query.
So originally, this query was a whole mess. I tried to apply a similar GROUP BY technique that fixed the first query to this query as well but it didn't really pan out.
Here is what I ended up with after I cleaned everything up:
BEGIN
DECLARE #LocalCompanyCode VARCHAR(5)
SET #LocalCompanyCode = '09'
DECLARE #LocalDivisionCode VARCHAR(5)
SET #LocalDivisionCode = '001'
DECLARE #LocalCustomerBaseFromDate DATETIME
SET #LocalCustomerBaseFromDate = '1/1/2018'
DECLARE #LocalCustomerBaseToDate DATETIME
SET #LocalCustomerBaseToDate = '9/1/2019'
DECLARE #LocalRecurringBaseFromDate DATETIME
SET #LocalRecurringBaseFromDate = '1/1/2017'
DECLARE #LocalLifetimeBaseFromDate DATETIME
SET #LocalLifetimeBaseFromDate = '1/1/2016'
SELECT
*
FROM (
SELECT
Email
,Date_Created
,BrandNewCustomer
,RecurringCustomer
,ReactivatedCustomer
,TotalOrders
,TotalCustomerValue
,TotalQuantity
,TotalOrdersNewBase
,TotalCustomerValueNewBase
,TotalQuantityNewBase
,TotalOrdersRecurringBase
,TotalCustomerValueRecurringBase
,TotalQuantityRecurringBase
,TotalOrdersLifetimeBase
,TotalCustomerValueLifetimeBase
,TotalQuantityLifetimeBase
,SUM(TotalCustomerValueNewBase) Over () BaseCustomersTotal
,SUM(TotalCustomerValueRecurringBase) Over () RecurringCustomersTotal
,SUM(TotalCustomerValueLifetimeBase) Over () LifetimeCustomersTotal
,SUM(TotalCustomerValue) Over () AllCustomersTotal
,(dense_rank() over (order by (case when Date_Created BETWEEN #LocalCustomerBaseFromDate and DATEADD(dayofyear, 1, #LocalCustomerBaseToDate) then Email end) asc) +
dense_rank() over (order by (case when Date_Created BETWEEN #LocalCustomerBaseFromDate and DATEADD(dayofyear, 1, #LocalCustomerBaseToDate) then Email end) desc) - 1
) as TotalCustomersOverCustomerBase
,(dense_rank() over (order by (case when Date_Created BETWEEN #LocalRecurringBaseFromDate and #LocalCustomerBaseFromDate then Email end) asc) +
dense_rank() over (order by (case when Date_Created BETWEEN #LocalRecurringBaseFromDate and #LocalCustomerBaseFromDate then Email end) desc) - 1
) as TotalCustomersOverRecurringBase
,(dense_rank() over (order by (case when Date_Created BETWEEN #LocalLifetimeBaseFromDate and #LocalRecurringBaseFromDate then Email end) asc) +
dense_rank() over (order by (case when Date_Created BETWEEN #LocalLifetimeBaseFromDate and #LocalRecurringBaseFromDate then Email end) desc) - 1
) as TotalCustomersOverLifetimeBase
,(DENSE_RANK() over (order by Email asc)
+DENSE_RANK() over ( order by Email desc)
- 1) as TotalCustomersOverBase
,SUM( CASE WHEN (BrandNewCustomer + RecurringCustomer + ReactivatedCustomer) = 1 THEN 1 ELSE 0 END) over () KeptCustomers
,SUM( CASE WHEN (BrandNewCustomer + RecurringCustomer + ReactivatedCustomer) = 0 THEN 1 ELSE 0 END) over () LostCustomers
FROM (
SELECT
T.Email
,MAX(T.Date_Created) Date_Created
,COUNT(*) TotalOrders
,SUM(T.Order_Sell_price) TotalCustomerValue
,SUM(T.Quantity_Ordered) TotalQuantity
,SUM(CASE WHEN T.Date_Created BETWEEN #LocalCustomerBaseFromDate and DATEADD(dayofyear, 1, #LocalCustomerBaseToDate) THEN 1 ELSE 0 END) TotalOrdersNewBase
,SUM(CASE WHEN T.Date_Created BETWEEN #LocalCustomerBaseFromDate and DATEADD(dayofyear, 1, #LocalCustomerBaseToDate) THEN Order_Sell_price ELSE 0 END) TotalCustomerValueNewBase
,SUM(CASE WHEN T.Date_Created BETWEEN #LocalCustomerBaseFromDate and DATEADD(dayofyear, 1, #LocalCustomerBaseToDate) THEN Quantity_Ordered ELSE 0 END) TotalQuantityNewBase
,SUM(CASE WHEN T.Date_Created BETWEEN #LocalRecurringBaseFromDate and #LocalCustomerBaseFromDate THEN 1 ELSE 0 END) TotalOrdersRecurringBase
,SUM(CASE WHEN T.Date_Created BETWEEN #LocalRecurringBaseFromDate and #LocalCustomerBaseFromDate THEN Order_Sell_price ELSE 0 END) TotalCustomerValueRecurringBase
,SUM(CASE WHEN T.Date_Created BETWEEN #LocalRecurringBaseFromDate and #LocalCustomerBaseFromDate THEN Quantity_Ordered ELSE 0 END) TotalQuantityRecurringBase
,SUM(CASE WHEN T.Date_Created BETWEEN #LocalLifetimeBaseFromDate and #LocalRecurringBaseFromDate THEN 1 ELSE 0 END) TotalOrdersLifetimeBase
,SUM(CASE WHEN T.Date_Created BETWEEN #LocalLifetimeBaseFromDate and #LocalRecurringBaseFromDate THEN Order_Sell_price ELSE 0 END) TotalCustomerValueLifetimeBase
,SUM(CASE WHEN T.Date_Created BETWEEN #LocalLifetimeBaseFromDate and #LocalRecurringBaseFromDate THEN Quantity_Ordered ELSE 0 END) TotalQuantityLifetimeBase
,CASE WHEN
( ISNULL(SUM(CASE WHEN T.Date_Created BETWEEN #LocalCustomerBaseFromDate and DATEADD(dayofyear, 1, #LocalCustomerBaseToDate) THEN 1 ELSE 0 END),0) >= 1
AND ISNULL(SUM(CASE WHEN T.Date_Created BETWEEN #LocalRecurringBaseFromDate and #LocalCustomerBaseFromDate THEN 1 ELSE 0 END),0) = 0
AND ISNULL(SUM(CASE WHEN T.Date_Created BETWEEN #LocalLifetimeBaseFromDate and #LocalRecurringBaseFromDate THEN Quantity_Ordered ELSE 0 END),0) = 0)
THEN 1 ELSE 0 END BrandNewCustomer
,CASE WHEN
( ISNULL(SUM(CASE WHEN T.Date_Created BETWEEN #LocalCustomerBaseFromDate and DATEADD(dayofyear, 1, #LocalCustomerBaseToDate) THEN 1 ELSE 0 END),0) >= 1
AND ISNULL(SUM(CASE WHEN T.Date_Created BETWEEN #LocalRecurringBaseFromDate and #LocalCustomerBaseFromDate THEN 1 ELSE 0 END),0) >= 1)
THEN 1 ELSE 0 END RecurringCustomer
,CASE WHEN
( ISNULL(SUM(CASE WHEN T.Date_Created BETWEEN #LocalCustomerBaseFromDate and DATEADD(dayofyear, 1, #LocalCustomerBaseToDate) THEN 1 ELSE 0 END),0) >= 1
AND ISNULL(SUM(CASE WHEN T.Date_Created BETWEEN #LocalRecurringBaseFromDate and #LocalCustomerBaseFromDate THEN 1 ELSE 0 END),0) = 0
AND ISNULL(SUM(CASE WHEN T.Date_Created BETWEEN #LocalLifetimeBaseFromDate and #LocalRecurringBaseFromDate THEN Quantity_Ordered ELSE 0 END),0) >= 1)
THEN 1 ELSE 0 END ReactivatedCustomer
FROM (
SELECT
F.Email
,F.Coal_Date Date_Created
,Month(F.Coal_Date) Month
,Year(F.Coal_Date) Year
,F.Customer_Purchase_Order_Number
,F.Order_Status
,Row_Number() over (Partition by Email order by Coal_Date asc) OrderCount
,F.Order_Sell_price
,F.Order_Quantity_Ordered Quantity_Ordered
FROM
FinalEcomTable F
WHERE
1=1
AND (F.Company_Code = #LocalCompanyCode OR #LocalCompanyCode IS NULL)
AND (F.Division_Code = #LocalDivisionCode OR #LocalDivisionCode IS NULL)
AND F.Coal_Date BETWEEN #LocalLifetimeBaseFromDate AND DATEADD(dayofyear, 1, #LocalCustomerBaseToDate)
AND F.Order_Status <> 'CANCELLED'
AND F.Odet_Line_Number = 1
) T
GROUP BY T.Email
) TT
) TTT
WHERE (BrandNewCustomer + RecurringCustomer + ReactivatedCustomer) = 1
ORDER BY email DESC
END
And here is the full execution plan:
https://www.brentozar.com/pastetheplan/?id=SkDyXPfDH
Those 4 dense_rank lines by themselves are almost doubling the run time. I realize now that its usually always these types of lines that do that.
I'll try to explain a bit about my thinking behind the query. So it is based on the same table as my first query. In reality, I am dealing with lots of data across three different linked servers, so I decided to create one master table that I would update every day and instead of calling to the linked servers and causing everything to run super slow, I would just call to that table.
So the most inner query takes each customer email, and then by each email, gets each order and what order number this is (first, second, etc). Is it a problem that I am doing Over () for my Row_Number aggregate? I would want to Group By Email but then how would I get each actual order number? Because if I group be email, then I have to do something like Max() of each order number, but I want the actual order number. Is there any way to do this?
Then the next outer query takes this data and actually groups by email. Over this group by I calculate totals over certain date ranges. The idea for this report is that there are three date ranges. New, which is what the actual report is based on and that range is something like the past 3 months. I look at all the customers in the past three months and then I see if they have any purchases in the recurring date range (a year before the three months) or the lifetime date range. Then I determine whether they are new customer or a recurring customer (or reactivated) by just putting a 1 on which type of customer it is.
Then my final outer query takes that data and calculates overall totals like Total value of all my recurring customers or total kept vs lost customers and so on. And like I mentioned before the dense_ranks really trip me up here. I feel like if I could add a Group By to this last query, I can solve a lot of my performance problems, but I can't seem to figure out how to integrate it.
Currently, this query runs about 10-15 seconds and I feel it could be instant. I tried creating the index the execution plan suggested but it didn't do anything.

MSSQL Group by and Select rows from grouping

I'm trying to figure out if what I'm trying to do is possible. Instead of resorting to multiple queries on a table, I wanted to group the records by business date and id then group by the id and select one date for a field and another date for the other field.
SELECT
*
{AMOUNT FROM DATE}
{AMOUNT FROM OTHER DATE}
FROM (
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
AS subquery
GROUP BY id
It seems that you're looking to do a pivot query. I usually use cross tabs for this. Based on the query you posted, it could look like:
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM (
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
)AS subquery
GROUP BY id;
You could also use a CTE.
WITH CTE AS(
SELECT
date,
id,
SUM(amount) AS amount
FROM
table
GROUP BY id, date
)
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM CTE
GROUP BY id;
Or even be a rebel and do the operation directly.
SELECT
id,
SUM(CASE WHEN date = '20190901' THEN amount ELSE 0 END) AmountFromSept01,
SUM(CASE WHEN date = '20191001' THEN amount ELSE 0 END) AmountFromOct01
FROM CTE
GROUP BY id;
However, some people have tested for performance and found that pre-aggregating can improve performance.
If I understand you correctly, then you're just trying to pivot, but only with two particular dates:
select id,
date1 = sum(iif(date = '2000-01-01', amount, null)),
date2 = sum(iif(date = '2000-01-02', amount, null))
from [table]
group by id

SQL joining most recent event by criteria to missing value

I have a SQL table that records interactions and the changes that happen in an interaction by interactionkey, user, group, and skill. I want to find the duration of each of the actions (A,B,C) by the grouping variables. Whenever action C happens, the skillKey is left blank (not NULL) and I need it to take the value of the last Skill in that interaction by the user and group so it is grouped together. The first table is the raw SQL data for 1 interaction, and the second is how I need it to look. Edit: I'm using Microsoft SQL Server Management Studio.
Here's what I have so far but it doesn't account for the last skill the user used in the group and interactionkey so it remains blank and adds it up seperately
SELECT
[InteractionKey],
[User],
[StartTime],
[SkillKey],
[GroupKey],
SUM(CASE WHEN ActionKey = 'A' THEN ActionDuration ELSE 0 END) AS 'ActionADuration',
SUM(CASE WHEN ActionKey = 'B' THEN ActionDuration ELSE 0 END) AS 'ActionBDuration',
SUM(CASE WHEN ActionKey = 'C' THEN ActionDuration ELSE 0 END) AS 'ActionCDuration'
FROM
(SELECT
[ActionKey],
[InteractionKey],
[SkillKey],
[GroupKey],
SUM(ActionDuration) AS 'ActionDuration',
[User],
CAST(StartTime AS DATE)
FROM
[InteractionTable]
GROUP BY
InteractionKey, User, SkillKey, GroupKey, ActionKey,
CAST(StartTime AS DATE)) sub
GROUP BY
InteractionKey, User, Date, SkillKey, GroupKey
ORDER BY
InteractionKey
Raw SQL Server table:
Desired output:
All you need is to prepare another "table" with SkillKey already filled as you need, and then use this table in your query.
I will use CTE in my code,
and it is different for divverent versions of SQL Server.
The first one is preferable, but it's for servers starting with 2012.
If you are on lower version use the second query.
-- for ##version >= 2012
with cte as
(
select *,
case
when SkillKey <> ''
then SkillKey
else lag(SkillKey) over(partition by InteractionKey, [User], GroupKey order by [Date])
end as SkillKey
from InteractionTable
)
SELECT
[InteractionKey],
[User],
[StartTime],
[SkillKey],
[GroupKey],
SUM(CASE WHEN ActionKey = 'A' THEN ActionDuration ELSE 0 END) AS 'ActionADuration',
SUM(CASE WHEN ActionKey = 'B' THEN ActionDuration ELSE 0 END) AS 'ActionBDuration',
SUM(CASE WHEN ActionKey = 'C' THEN ActionDuration ELSE 0 END) AS 'ActionCDuration'
FROM
(SELECT
[ActionKey],
[InteractionKey],
[SkillKey],
[GroupKey],
SUM(ActionDuration) AS 'ActionDuration',
[User],
CAST(StartTime AS DATE)
FROM
cte
GROUP BY
InteractionKey, User, SkillKey, GroupKey, ActionKey,
CAST(StartTime AS DATE)) sub
GROUP BY
InteractionKey, User, Date, SkillKey, GroupKey
ORDER BY
InteractionKey
The second:
-- for ##version >= 2005
with r as
(
select *,
row_number() over(partition by InteractionKey, [User], GroupKey order by [Date]) as rn
from InteractionTable
)
,cte as
(
select r1.*,
case
when r1.SkillKey <> ''
then r1.SkillKey
else r2.SkillKey
end as SkillKey
from r r1
left join r r2
on r1.rn = r2.rn + 1
)
SELECT
[InteractionKey],
[User],
[StartTime],
[SkillKey],
[GroupKey],
SUM(CASE WHEN ActionKey = 'A' THEN ActionDuration ELSE 0 END) AS 'ActionADuration',
SUM(CASE WHEN ActionKey = 'B' THEN ActionDuration ELSE 0 END) AS 'ActionBDuration',
SUM(CASE WHEN ActionKey = 'C' THEN ActionDuration ELSE 0 END) AS 'ActionCDuration'
FROM
(SELECT
[ActionKey],
[InteractionKey],
[SkillKey],
[GroupKey],
SUM(ActionDuration) AS 'ActionDuration',
[User],
CAST(StartTime AS DATE)
FROM
cte
GROUP BY
InteractionKey, User, SkillKey, GroupKey, ActionKey,
CAST(StartTime AS DATE)) sub
GROUP BY
InteractionKey, User, Date, SkillKey, GroupKey
ORDER BY
InteractionKey

How to get count of a particular row

I have table that contain Id,Date and Status i.e open/close
i just want a result in sql that contain month wise open,close and total count of Id's
e.g In Jan open count 15,close count 5 and total count 20
Use RollUp() and Group By as below:
;WITH T AS
(
SELECT
Id,
DATENAME(MONTH,[Date]) AS [MonthName],
Status
FROM #tblTest
)
SELECT
[MonthName],
[Status],
StatusCount
FROM
(
SELECT
MonthName,
CASE ISNULL(Status,'') WHEN '' THEN 'Total' ELSE Status END AS Status,
Count(Status) AS StatusCount
FROM T
GROUP BY ROLLUP([MonthName],[Status])
)X
WHERE X.MonthName IS NOT NULL
ORDER BY X.[MonthName],X.[Status]
Output:
Note: If required data in single row by month then apply PIVOT
select year(date), month(date),
sum(case when status = 'open' then 1 else 0 end) as open_count,
sum(case when status = 'closed' then 1 else 0 end) as closed_count,
count(*) as total_count
from your_table
group by year(date), month(date)

Multiple sub queries

Is it possible to get the result as below from the same table date-wise records:
Enrolled Enrolled as Email Enrolled as Text Deals Redeemed
<First Date> 7 5 2 6
<Next Date> 9 3 6 14
Table structure look something like this:
Customer_id, field1, field2, responsecode, created_date
My current query is something like this:
Select
Created,
Enroll = (Select COUNT(*) from tblCustomer where field1 <> '' group by created),
Email = (Select COUNT(field1) from tblCustomer where field1 = 'E-mail' and field1 <> '' group by created),
Cell = (Select COUNT(*) from tblCustomer where field1 = 'Cell Phone' and field1 <> '' group by created)
from tblCustomer
group by created
order by created
You don't want a COUNT(), but instead, a SUM( CASE/WHEN )
Select
Created,
count(*) TotalCnt,
SUM( CASE WHEN Field1 = 'E-mail' then 1 else 0 END ) as EMailCnt,
SUM( CASE WHEN Field1 = 'Cell Phone' then 1 else 0 END ) as CellCnt,
SUM( CASE WHEN RedeamedCondition then 1 else 0 END ) as RedeamCnt
from
tblCustomer
group by
created
order by
created
Note... if created is a date/time you will need to have the group by based on the date portion ONLY of the "created", otherwise you would get different counts for every second... From another post, the following gets only the date portion of a date time by basically removing the hours:minutes:seconds portion
DATEADD(dd, 0, DATEDIFF(dd, 0, created))
or if that doesn't make sense, you could just do it by
datepart( yy, created) as GrpYear,
datepart( mm, created) as GrpMonth,
datepart( dd, created) as GrpDay, ... rest of columns.....
Try:
select created_date,
count(field1) Enrolled,
count(case field1 when 'E-mail' then 1 end) Enrolled_as_Email,
count(case field1 when 'Cell Phone' then 1 end) Enrolled_as_Cell,
(Select COUNT(*)
from tbl_TransactionDishout d
where d.created = c.created_date and
d.DishoutResponseCode = '0000') Deals_Redeemed
from tblCustomer c
group by created_date
order by created_date