I have a table with columns LOB, Report Date, and Indicator.
LOB | Report Date | Indicator
-------------------+------------------------------
CNS |2022-07-20 | 0
MNR |2022-07-11 | 0
MNR |2022-07-10 | 0
MNR |2022-07-04 | 0
CNS |2022-07-01 | 0
CNS |2022-01-26 | 0
MNR |2022-01-03 | 0
MNR |2022-01-03 | 0
BBP |2021-12-29 | 0
BBP |2021-12-22 | 0
BBP |2021-12-21 | 0
BBP |2021-12-16 | 0
MNR |2021-11-11 | 0
MNR |2021-11-06 | 0
MNR |2021-11-02 | 0
OUTPUT:
LOB | Report Date | Indicator
-------------------+------------------------------
CNS |2022-07-20 | 1
MNR |2022-07-11 | 0
MNR |2022-07-10 | 0
MNR |2022-07-04 | 1
CNS |2022-07-01 | 0
CNS |2022-01-26 | 1
MNR |2022-01-03 | 0
MNR |2022-01-03 | 1
BBP |2021-12-29 | 0
BBP |2021-12-22 | 0
BBP |2021-12-21 | 0
BBP |2021-12-16 | 1
MNR |2021-11-11 | 0
MNR |2021-11-06 | 0
MNR |2021-11-02 | 0
Need to Update the indicator column based on LOB change. When ever the LOB change is happened then I need to update the Indictor column value to “1” or else the default Indicator column value is “0”
For example, in the bottom of the table the LOB starts with "MNR" then later the LOB changed to "BBP" here I need to Update as "1" ... In the same way from "BBP" it got changed to "MNR" here I need to update as "1" ... When ever the change of LOB happened I need to update the Indicator as "1"
Kindly help me with the Solution.
I think something like this should work.
WITH cte as (
select LOB,
Report_date,
CASE
WHEN LOB <> lead(LOB) OVER (order by Report_date DESC)
THEN 1
ELSE 0
END as Indicator
from tableA
)
UPDATE tableA set tableA.Indicator = cte.Indicator
FROM cte
WHERE tableA.LOB = cte.LOB and tableA.Report_date = cte.Report_date
db fiddle
And as #stu mentioned. you could just directly update the cte
WITH cte as (
select LOB,
Report_date, Indicator,
CASE
WHEN LOB <> lead(LOB) OVER (order by Report_date DESC)
THEN 1
ELSE 0
END as NewIndicator
from tableA
)
UPDATE cte set Indicator = NewIndicator
Related
If running speed of a day is > 0, its status is true.
In case of no data for a particular day, infer last status code (another
column in table) value recorded and if the value is zero then mark run status as true. For codes other than zero mark status as false.
See the table below
Day Run speed Statuscode Status
---------------------------------------
1-Jan-14 55 0 TRUE
2-Jan-14 60 0 TRUE
3-Jan-14 58 0 TRUE
4-Jan-14 61 0 TRUE
5-Jan-14 57 0 TRUE
6-Jan-14 56 0 TRUE
7-Jan-14 60 0 TRUE
8-Jan-14 TRUE
9-Jan-14 TRUE
10-Jan-14 55 0 TRUE
11-Jan-14 56 0 TRUE
12-Jan-14 60 0 TRUE
13-Jan-14 0 20 FALSE
(example 8-jan-14/9-jan-14 does not have value but since 7th was last recorded and it was true that is why the status of those two dates are true as well)
Use OUTER APPLY as more flexible lag
Live test: http://sqlfiddle.com/#!18/4c73f/18
select
o.*,
Status =
convert(bit,
case
when o.RunningSpeed > 0
or o.RunningSpeed is null and prev.StatusCode = 0 then
1
else
0
end)
from tbl o
outer apply
(
select top 1 StatusCode
from tbl i
where i.Day < o.Day and i.StatusCode is not null
order by i.Day desc
) prev
Output:
| Day | RunningSpeed | Statuscode | Status |
|----------------------|--------------|------------|--------|
| 2014-01-01T00:00:00Z | 55 | 0 | true |
| 2014-01-02T00:00:00Z | 60 | 0 | true |
| 2014-01-03T00:00:00Z | 58 | 0 | true |
| 2014-01-04T00:00:00Z | 61 | 0 | true |
| 2014-01-05T00:00:00Z | 57 | 0 | true |
| 2014-01-06T00:00:00Z | 56 | 0 | true |
| 2014-01-07T00:00:00Z | 60 | 0 | true |
| 2014-01-08T00:00:00Z | (null) | (null) | true |
| 2014-01-09T00:00:00Z | (null) | (null) | true |
| 2014-01-10T00:00:00Z | 55 | 0 | true |
| 2014-01-11T00:00:00Z | 56 | 0 | true |
| 2014-01-12T00:00:00Z | 60 | 0 | true |
| 2014-01-13T00:00:00Z | 0 | 20 | false |
A CASE statement will solve this for you. And OUTER APPLY will solve the second requirement.
APPLY, CROSS APPLY and OUTER APPLY as the most understood and under-utilized iterators in my opinion. But once you grasp their power and understand the use cases, they become lethal weapons in your toolbox. They are especially useful in TOP(n) [by some logic] situations, where there built-in functions fail to meet your requirements.
Note the use of BIT to represent the boolean TRUE/FALSE
SELECT Day
, RunningSpeed
, PreviousRunningSpeed
, CASE
WHEN RunningSpeed IS NULL AND PreviousRunningSpeed > 0 THEN 1
WHEN RunningSpeed > 0 THEN 1
ELSE 0
END AS Status
FROM (SELECT Day
, RunningSpeed
, d.RunningSpeed AS PreviousRunningSpeed
FROM tbl AS t1
OUTER APPLY (SELECT TOP(1)
RunningSpeed
FROM tbl AS t2
WHERE RunningSpeed IS NOT NULL
AND t2.Day < t1.Day
ORDER BY Day ASC)) AS d
What you really want for this is LAG(IGNORE NULLS) but SQL Server does not support that.
You can use a different trick, which is to get the maximum date where the status is 0 and the maximum date where the status is not 0 and compare them. So:
select t.*,
(case when statuscode = 0 then 'TRUE'
when statuscode <> 0 then 'FALSE'
when (max(case when statuscode = 0 then day end) over (order by day) >
coalesce(max(case when statuscode <> 0 day date end) over (order by day), '2000-01-01')
)
then 'TRUE'
else 'FALSE'
end) as status
from t;
Here is a db<>fiddle.
You can actually simplify this to just the last conditions:
select t.*,
(case when (max(case when statuscode = 0 then day end) over (order by day) >
coalesce(max(case when statuscode <> 0 then day end) over (order by day), '2000-01-01')
)
then 'TRUE'
else 'FALSE'
end) as status
from t;
The first version better follows the logic that you describe.
I cannot tell from the question whether you want to base the logic on runningspeed or statuscode. The logic would be quite similar for runningspeed.
Below is some repro code for an issue I am having.
Run it in SQL SERVER 2017 you will get different (and incorrect) result compared with any other SQL SERVER version Setting the database to lower compatibility level on the sql Server 2017 instance, it works fine too.
Why does this happen and how can I fix it without changing the compatibility level?
Actual Result
+--------------+--------------+----------------+---------+-----------+---------+------------+-------+
| IsPriorAfter | IsIdealAfter | IsCurrentAfter | IsPrior | IsCurrent | IsIdeal | SecurityID | PosID |
+--------------+--------------+----------------+---------+-----------+---------+------------+-------+
| 1 | 1 | 1 | 1 | 1 | 1 | 123 | 1 |
| 0 | 0 | 0 | 0 | 1 | 1 | 234 | 2 |
| 0 | 0 | 0 | 1 | 0 | 0 | 234 | 3 |
+--------------+--------------+----------------+---------+-----------+---------+------------+-------+
Expected Result
+--------------+--------------+----------------+---------+-----------+---------+------------+-------+
| IsPriorAfter | IsIdealAfter | IsCurrentAfter | IsPrior | IsCurrent | IsIdeal | SecurityID | PosID |
+--------------+--------------+----------------+---------+-----------+---------+------------+-------+
| 1 | 1 | 1 | 1 | 1 | 1 | 123 | 1 |
| 0 | 1 | 1 | 0 | 1 | 1 | 234 | 2 |
| 1 | 0 | 0 | 1 | 0 | 0 | 234 | 3 |
+--------------+--------------+----------------+---------+-----------+---------+------------+-------+
Repro
if object_id('ForSubQuery') is not null begin
DROP TABLE ForSubQuery
end
Create Table ForSubQuery
(
SecID int
)
INSERT INTO ForSubQuery SELECT 123
INSERT INTO ForSubQuery SELECT 234
GO
SELECT * FROM ForSubQuery
if object_id('MainTable') is not null begin
DROP TABLE MainTable
end
Create Table MainTable
(
IsPrior bit,
IsCurrent bit,
IsIdeal bit,
[SecurityID] int,
PosID int
)
INSERT INTO MainTable SELECT 1,1,1,123,1
INSERT INTO MainTable SELECT 0,1,1,234,2
INSERT INTO MainTable SELECT 1,0,0,234,3
GO
SELECT * FROM MainTable
SELECT
CASE
WHEN
Position.IsPrior = 1
AND Position.[SecurityID] in (SELECT
SecID
FROM ForSubQuery
)
THEN 1
ELSE 0
END AS IsPriorAfter
,CASE
WHEN
Position.IsIdeal = 1
AND [Position].[SecurityID] IN (SELECT
secid
FROM ForSubQuery
)
THEN 1
ELSE 0
END AS IsIdealAfter
,CASE
WHEN
Position.IsCurrent = 1
AND [Position].[SecurityID] IN (SELECT
secid
FROM ForSubQuery
)
THEN 1
ELSE 0
END AS IsCurrentAfter
, Position.*
FROM MainTable [Position]
order by Position.PosID
TLDR
This is a bug that has been fixed in CU8 so installing at least that CU and ideally the most recent one will fix it.
Pre SQL Server 2017
In SQL Server 2016 the plan looks as above. The IN is treated the same as EXISTS so it evaluates the following three columns.
CASE WHEN IsPrior = 1 AND EXISTS (SELECT * FROM ForSubQuery WHERE SecID = MainTable.SecurityID) THEN 1 ELSE 0 END AS IsPriorAfter
CASE WHEN IsIdeal = 1 AND EXISTS (SELECT * FROM ForSubQuery WHERE SecID = MainTable.SecurityID) THEN 1 ELSE 0 END AS IsIdealAfter
CASE WHEN IsCurrent = 1 AND EXISTS (SELECT * FROM ForSubQuery WHERE SecID = MainTable.SecurityID) THEN 1 ELSE 0 END AS IsCurrentAfter
Each subquery instance gets its own operator in the plan and the query returns the correct result but this is sub optimal as the identical subquery may be executed up to three times per row.
Because each sub query has an AND next to it SQL Server can skip evaluating the sub query if the result of that expression is false however. This is achieved by each nested loops containing a pass through predicate. For example the one corresponding to evaluation of IsPriorAfter has a pass through predicate of IsFalseOrNull (IsPrior=1)
IsPrior=1 is a boolean expression that can return false, null, or true. The IsFalseOrNull then inverts the result and returns 1 for false, null and 0 for true. So the pass through predicate evaluates to true/1 if IsPrior is anything other than 1 (including NULL) and would then skip executing the sub query.
SQL Server 2017 RTM
SQL Server 2017 introduces a new optimisation rule CollapseIdenticalScalarSubquery. In the RTM version the execution plan is not correct.
Problem Plan
The sub query is now in a single operator and the pass through predicates are combined
IsFalseOrNull([IsCurrent]=(1)) OR IsFalseOrNull([IsIdeal]=(1)) OR IsFalseOrNull([IsPrior]=(1))
However this condition is not correct! It evaluates to true unless all three of IsPrior, IsIdeal, IsCurrent are 1.
So in your case the sub query is only executed once (for the first row in the table - where all three of the columns are equal to 1).
For the two other rows it should be executed but isn't. The nested loops has a probe column that is set to 1 if the correlated subquery returns a row. (Labelled Expr1016 in the plan). When execution is skipped this probe column is set to NULL
The final compute scalar in the plan has the following expression. When Expr1016 is null this evaluates to 0 for all three of your calculated columns using CASE.
[Expr1005] = Scalar Operator(CASE WHEN [IsPrior]=(1) AND [Expr1016] THEN (1) ELSE (0) END),
[Expr1009] = Scalar Operator(CASE WHEN [IsIdeal]=(1) AND [Expr1016] THEN (1) ELSE (0) END),
[Expr1013] = Scalar Operator(CASE WHEN [IsCurrent]=(1) AND [Expr1016] THEN (1) ELSE (0) END)
SQL Server 2017 patched
The final fixed plan after the CU is applied has the same plan shape as the 2017 RTM plan (with the subquery only appearing once) but the pass through predicate is now
IsFalseOrNull([IsCurrent]=(1)) AND IsFalseOrNull([IsIdeal]=(1)) AND IsFalseOrNull([IsPrior]=(1))
This only evaluates to true if none of those columns have a value of 1 so the sub query is now evaluated exactly when needed.
Currently, I have a table that looks like below:
ID|Date |Val
1 |1/1/2016|1
2 |1/1/2016|0
3 |1/1/2016|0
1 |2/1/2016|0
2 |2/1/2016|1
3 |2/1/2016|1
1 |3/1/2016|0
2 |3/1/2016|0
3 |3/1/2016|0
I want to update it so that the value carries over for each ID, but not on earlier dates than when the value first appeared. Also, the value can only change 0 to 1, not vice versa. So the final product would look like:
ID|Date |Val
1 |1/1/2016|1
2 |1/1/2016|0
3 |1/1/2016|0
1 |2/1/2016|1
2 |2/1/2016|1
3 |2/1/2016|1
1 |3/1/2016|1
2 |3/1/2016|1
3 |3/1/2016|1
I've tried a few code combinations, but the conditional of carrying the value after the date where the value first appears is tripping me up. I'd appreciate any help!
In SQL Server 2012+, using the aggregate max() as a window function with over() (inside a common table expression to simplify the update):
;with cte as (
select *
, MaxVal = max(convert(int,val)) over (partition by id order by date)
from t
)
update cte
set val = maxVal
where val <> maxVal
rextester demo: http://rextester.com/ZPGWB94088
result:
+----+------------+-----+
| id | Date | Val |
+----+------------+-----+
| 1 | 2016-01-01 | 1 |
| 2 | 2016-01-01 | 0 |
| 3 | 2016-01-01 | 0 |
| 1 | 2016-02-01 | 1 |
| 2 | 2016-02-01 | 1 |
| 3 | 2016-02-01 | 1 |
| 1 | 2016-03-01 | 1 |
| 2 | 2016-03-01 | 1 |
| 3 | 2016-03-01 | 1 |
+----+------------+-----+
Prior to SQL Server 2012, you could use something like this:
update t
set Val = 1
from t
inner join (
select i.Id, min(i.Date) as Date
from t as i
where i.Val = 1
group by i.Id
) as m
on t.Id = m.Id
and t.Date >= m.Date
and t.Val = 0
rextester demo: http://rextester.com/RLEAO15622
I am currently having difficulty getting the correct values from my table. Here is my table
NOTE: The column Status has 3 possible values (Cleaned, Unclean, Closed)
+-----------+-------------+--------+------------+
|ApplicantID|ApplicantName| Status | HireDate |
+-----------+-------------+--------+------------+
| 1 | John Smith |Cleaned |08/26/2015 |
| 2 | Alex Murphy |Closed |09/12/2015 |
| 3 | Oliver David|Cleaned |01/11/2015 |
| 4 | Max Payne |Unclean |03/18/2015 |
+-----------+-------------+--------+------------+
The output I'm expecting and it should also be sorted by year.
For example I call all these records for the year 2015 which I get using the variable #Year.
NOTE: The column Total is the SUM of Cleaned and Unclean
+---------+-----------+-----------+----------+---------+
| Month | Cleaned | Unclean | Closed | Total |
+---------+-----------+-----------+----------+---------+
| January| 1 | 0 | 0 | 1 |
| February| 0 | 0 | 0 | 0 |
| March | 0 | 1 | 0 | 1 |
| April | 0 | 0 | 0 | 0 |
| May | 0 | 0 | 0 | 0 |
| June | 0 | 0 | 0 | 0 |
| July | 0 | 0 | 0 | 0 |
| August | 1 | 0 | 0 | 1 |
|September| 0 | 0 | 1 | 0 |
| October| 0 | 0 | 0 | 0 |
| November| 0 | 0 | 0 | 0 |
| December| 0 | 0 | 0 | 0 |
+---------+-----------+-----------+----------+---------+
I can't seem to get the right code, for the sql this is my current code.
SELECT Month(HireDate) AS Month, COUNT(*)
FROM Hires
GROUP BY Month(HireDate)
I know my coding is wrong, because it is incomplete.
Generate a list of numbers from 1 to 12 first to hold all months. Then do a LEFT JOIN on Hires to make sure all missing months are accounted for. Then use conditional aggregation for the totals:
SQL Fiddle
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
)
SELECT
Month = DATENAME(MONTH, DATEADD(MONTH, N-1,0)),
Cleaned = SUM(CASE WHEN h.Status = 'Cleaned' THEN 1 ELSE 0 END),
Closed = SUM(CASE WHEN h.Status = 'Closed' THEN 1 ELSE 0 END),
Unclean = SUM(CASE WHEN h.Status = 'Unclean' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN h.Status IN('Cleaned', 'Unclean') THEN 1 ELSE 0 END)
FROM CteMonths m
LEFT JOIN Hires h
ON m.N = MONTH(h.HireDate)
--AND YEAR(h.HireDate) = #year --uncomment this line to filter for year.
GROUP BY m.N
ORDER BY m.N
If you want to include the YEAR:
SQL Fiddle
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
),
CteYears(yr) AS(
SELECT DISTINCT YEAR(HireDate) FROM Hires
),
CteAllDates(dt) AS(
SELECT
DATEADD(MONTH, m.N - 1, DATEADD(YEAR, y.yr - 1900, 0))
FROM CteMonths m
CROSS JOIN CteYears y
)
SELECT
Year = YEAR(d.dt),
Month = DATENAME(MONTH, d.dt),
Cleaned = SUM(CASE WHEN h.Status = 'Cleaned' THEN 1 ELSE 0 END),
Closed = SUM(CASE WHEN h.Status = 'Closed' THEN 1 ELSE 0 END),
Unclean = SUM(CASE WHEN h.Status = 'Unclean' THEN 1 ELSE 0 END),
Total = SUM(CASE WHEN h.Status IN('Cleaned', 'Unclean') THEN 1 ELSE 0 END)
FROM CteAllDates d
LEFT JOIN Hires h
ON MONTH(d.dt) = MONTH(h.HireDate)
AND YEAR(d.dt) = YEAR(h.HireDate)
GROUP BY YEAR(d.dt), MONTH(d.dt), DATENAME(MONTH, d.dt)
ORDER BY YEAR(d.dt), MONTH(d.dt)
If you want to filter for year, say #year = 2015, you can replace the previous ctes with:
;WITH CteMonths AS(
SELECT * FROM(VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
)t(N)
),
CteAllDates(dt) AS(
SELECT
DATEADD(MONTH, m.N - 1, DATEADD(YEAR, #year - 1900, 0))
FROM CteMonths m
)...
I suggest to create TEMP table with values from 1 to 12 (numbers of months) and JOIN your table with TEMP table. To achieve values as columns names you can use PIVOT or CASE. You can do It in following:
INSERT INTO #Months VALUES
(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
SELECT DATENAME(MONTH, DATEADD(MONTH, m.Id-1, 0)) AS [Month]
, SUM(CASE WHEN [Status] = 'Cleaned' THEN 1 ELSE 0 END) AS [Cleaned]
, SUM(CASE WHEN [Status] = 'Closed' THEN 1 ELSE 0 END ) AS [Closed]
, SUM(CASE WHEN [Status] = 'Unclean' THEN 1 ELSE 0 END) AS [Unclean]
, SUM(CASE WHEN [Status] IN ('Unclean', 'Cleaned') THEN 1 ELSE 0 END) AS [Total]
FROM #Test t
RIGHT JOIN #Months m ON m.Id = MONTH(t.HireDate)
GROUP BY m.Id
OUTPUT
+---------+-----------+-----------+----------+---------+
| Month | Cleaned | Unclean | Closed | Total |
+---------+-----------+-----------+----------+---------+
| January | 1 | 0 | 0 | 1 |
| February| 0 | 0 | 0 | 0 |
| March | 0 | 1 | 0 | 1 |
| April | 0 | 0 | 0 | 0 |
| May | 0 | 0 | 0 | 0 |
| June | 0 | 0 | 0 | 0 |
| July | 0 | 0 | 0 | 0 |
| August | 1 | 0 | 0 | 1 |
|September| 0 | 0 | 1 | 0 |
| October | 0 | 0 | 0 | 0 |
| November| 0 | 0 | 0 | 0 |
| December| 0 | 0 | 0 | 0 |
+---------+-----------+-----------+----------+---------+
DEMO
You can test It at: SQL FIDDLE
Im trying to build by day report at the moment the code im using works but nnot to what i need it to do.
SELECT Notices.Promoter,
Sum(IIf([Notices].[Type]='GRANT PERMIT' Or [Notices].[Type]='GRANT VARIATION' Or [Notices].[Notice Type]='GRANT PAA',1,0)) AS Granted,
Sum(IIf([Notices].[Type]='REFUSE APPLICATION',1,0)) AS Refused,
Sum(IIf([Notices].[Status]='Deemed',1,0)) AS Deemed,
Sum(IIf([Notices].[Error]<>"" And Notices.[Category]<>"Observation" And Notices.[Category]<>"S.74 Overrun",1,0)) AS [Potential Penalty]
FROM Notices
WHERE (((Notices.[Day Of Week])=[TempVars]![DayReport]))
GROUP BY Notices.Promoter;
the tempVar [DayReport] is the day of the week mon,tue,wed.... sun
and the code outputs this
Promoter | Granted | Refused | Deemed | Potential Penalty
Name | 0 | 0 | 0 | 0
Name2 | 3 | 0 | 0 | 0
Name3 | 4 | 2 | 1 | 0
Name4 | 0 | 1 | 1 | 0
Name5 | 1 | 0 | 0 | 0
What i want is it to not show the Promoter that has all 0's in the fields like this
Promoter | Granted | Refused | Deemed | Potential Penalty
Name2 | 3 | 0 | 0 | 0
Name3 | 4 | 2 | 1 | 0
Name4 | 0 | 1 | 1 | 0
Name5 | 1 | 0 | 0 | 0
As im not sure about how to go about this i thought i would put it to the wonderful people of Stackoverflow
SELECT *
FROM
(
SELECT Notices.Promoter,
Sum(IIf([Notices].[Type]='GRANT PERMIT' Or [Notices].[Type]='GRANT VARIATION' Or [Notices].[Notice Type]='GRANT PAA',1,0)) AS Granted,
Sum(IIf([Notices].[Type]='REFUSE APPLICATION',1,0)) AS Refused,
Sum(IIf([Notices].[Status]='Deemed',1,0)) AS Deemed,
Sum(IIf([Notices].[Error]<>"" And Notices.[Category]<>"Observation" And Notices.[Category]<>"S.74 Overrun",1,0)) AS [Potential Penalty]
FROM Notices
WHERE (((Notices.[Day Of Week])=[TempVars]![DayReport]))
GROUP BY Notices.Promoter
) s
WHERE Promoter <> 0
Refused <> 0 AND
Deemed <> 0 AND
[Potential Penalty] <> 0
UPDATE 1
SELECT Notices.Promoter,
Sum(IIf([Notices].[Type]='GRANT PERMIT' Or [Notices].[Type]='GRANT VARIATION' Or [Notices].[Notice Type]='GRANT PAA',1,0)) AS Granted,
Sum(IIf([Notices].[Type]='REFUSE APPLICATION',1,0)) AS Refused,
Sum(IIf([Notices].[Status]='Deemed',1,0)) AS Deemed,
Sum(IIf([Notices].[Error]<>"" And Notices.[Category]<>"Observation" And Notices.[Category]<>"S.74 Overrun",1,0)) AS [Potential Penalty]
FROM Notices
WHERE (((Notices.[Day Of Week])=[TempVars]![DayReport]))
GROUP BY Notices.Promoter
HAVING Sum(IIf([Notices].[Type]='GRANT PERMIT' Or [Notices].[Type]='GRANT VARIATION' Or [Notices].[Notice Type]='GRANT PAA',1,0)) <> 0 AND
Sum(IIf([Notices].[Type]='REFUSE APPLICATION',1,0)) <> 0 AND
Sum(IIf([Notices].[Status]='Deemed',1,0)) <> 0 AND
Sum(IIf([Notices].[Error]<>"" And Notices.[Category]<>"Observation" And Notices.[Category]<>"S.74 Overrun",1,0)) <> 0