Trying to set a varable based on the current date and pass to query wihtin SQL function. It error at the IF statement. Any ideas?
CREATE FUNCTION CS_AwaredRCPs
(
#currentDate DATE,
#fiscalYear INT
IF DATEPART(m,#currentDate) > 10
SET #fiscalYear = DATEPART(yyyy,#currentDate)
ELSE
SET #fiscalYear = DATEPART(yyyy,#currentDate) - 1
END
)
RETURNS TABLE
AS
RETURN
(
SELECT dbo.tbl_requirementManagement.postaward_specialist_id,
SUM(dbo.tbl_requirementManagement.actual_award_value) AS AwardValue,
COUNT(dbo.tbl_requirementManagement.postaward_specialist_id) AS AwardCount
FROM dbo.tbl_requirementManagement RIGHT OUTER JOIN
dbo.vw_ContractSpecialists ON dbo.tbl_requirementManagement.postaward_specialist_id = dbo.vw_ContractSpecialists.user_certificateSerialNumber
GROUP BY dbo.tbl_requirementManagement.statusID, dbo.tbl_requirementManagement.postaward_specialist_id, dbo.tbl_requirementManagement.fiscal_year
HAVING (dbo.tbl_requirementManagement.statusID = 4) AND
(dbo.tbl_requirementManagement.postaward_specialist_id <> 0) AND
(dbo.tbl_requirementManagement.fiscal_year = #fiscalYear)
)
`
You don't need to use IF at all, you can simply make your predicate:
fiscal_year = DATEPART(YEAR, #currentDate)
+ CASE WHEN DATEPART(MONTH, #CurrentDate) <= 10 THEN -1 ELSE 0 END
Then you don't need the #FiscalYear parameter at all. For what it is worth there are a number of other things you should change in your function
Use table aliases to significantly reduce the amount of text, so rather than using dbo.tbl_requirementManagement over and over again, you could just use rm
The fact that you reference your outer table in the where clause, turns your RIGHT OUTER JOIN into an INNER JOIN so you may as well just use and INNER JOIN. To clarify, you have (dbo.tbl_requirementManagement.statusID = 4), so where you have no match in tbl_requirementManagement, statusID will be NULL, and NULL = 4 returns NULL, which is not true, therefore the row would not be returned.
Your predicates do not reference aggregates so should be in the WHERE clause, not the HAVING
So your final function becomes:
CREATE FUNCTION CS_AwaredRCPs (#currentDate DATE)
RETURNS TABLE
AS
RETURN
( SELECT rm.postaward_specialist_id,
SUM(rm.actual_award_value) AS AwardValue,
COUNT(rm.postaward_specialist_id) AS AwardCount
FROM dbo.tbl_requirementManagement AS rm
INNER JOIN dbo.vw_ContractSpecialists AS cs
ON rm.postaward_specialist_id = cs.user_certificateSerialNumber
WHERE rm.fiscal_year = DATEPART(YEAR,#currentDate)
+ CASE WHEN DATEPART(MONTH, #CurrentDate) <= 10 THEN -1 ELSE 0 END
AND rm.statusID = 4
AND rm.postaward_specialist_id <> 0
GROUP BY rm.statusID, rm.postaward_specialist_id, rm.fiscal_year
);
You cannot do a if-else check inside Function parameter list. Move the If-else part to the function body. Try this.
CREATE FUNCTION Cs_awaredrcps (#currentDate DATE)
RETURNS #ret_table TABLE (
postaward_specialist_id INT,
AwardValue INT,
AwardCount INT )
AS
BEGIN
DECLARE #fiscalYear INT
IF Datepart(Month, #currentDate) > 10
SET #fiscalYear = Datepart(yyyy, #currentDate)
ELSE
SET #fiscalYear = Datepart(yyyy, #currentDate) - 1
INSERT INTO #ret_table
SELECT dbo.tbl_requirementManagement.postaward_specialist_id,
Sum(dbo.tbl_requirementManagement.actual_award_value) AS AwardValue,
Count(dbo.tbl_requirementManagement.postaward_specialist_id) AS AwardCount
FROM dbo.tbl_requirementManagement
RIGHT OUTER JOIN dbo.vw_ContractSpecialists
ON dbo.tbl_requirementManagement.postaward_specialist_id = dbo.vw_ContractSpecialists.user_certificateSerialNumber
GROUP BY dbo.tbl_requirementManagement.statusID,
dbo.tbl_requirementManagement.postaward_specialist_id,
dbo.tbl_requirementManagement.fiscal_year
HAVING ( dbo.tbl_requirementManagement.statusID = 4 )
AND ( dbo.tbl_requirementManagement.postaward_specialist_id <> 0 )
AND ( dbo.tbl_requirementManagement.fiscal_year = #fiscalYear )
RETURN
END
Related
I'm obviously new to this and I need some help. I have this code in SQL Server Management Studio.
The error that I get is
Msg 156, Level 15, State 1, Line 38
Incorrect syntax near the keyword 'IF'
If I execute direct select statement instead of using "If" the query is successfully executed.
Please point out what I am missing and where is my mistake.
Thanks a lot.
declare #dateFrom [date] = cast(dateadd(day, -7, getdate()) as [date]);
declare #dateTo [date] = cast(dateadd(day, 28, getdate()) as [date]);
declare #prTypeCode [varchar](10) = null;
declare #ExcludeA [bit] = 0 ;
declare #ExcludeB [bit] = 0;
;with distribution as
(
select *
from [schema1].[Table1]
),
product as
(
select *
from [schema1].[Product]
),
fint as
(
select
*,
(case when CHARINDEX('0%', FIN_ CODE) > 0 then 1 else 0 end) as ZFlag
from [schema1].[FinTab]
),
deal as
(
select
*,
case when (AG_ID = 5 or DE_ID = 6) then 1
else 0 end as Ex_flag
from
[P_ BASE1].[ schema2].[table3]
),
dealersn as
(
select distinct *
from [P_ BASE1].[ schema2].[table4]
),
regionN as
(
select distinct *
from [P_ BASE1].[ schema2].[table5]
),
final as
(
select
d.PRODUCT_CODE, p.PR_NAME, p.PR_TYPE_CODE, p.PR_ACTIVE,
d.FIN_TABLE_CODE, ft.FT_NAME, ft.FT_ACTIVE,
cast(d.SND_TO_DATE as [date]) as SND_TO_DATE,
count(d.SN_ID) as SHOP_COUNT,
min(ft.ZFlag) as ZFlag,
min(ds.Ex_flag) as ExFlag, dn.SND_FULLNAME,rn.REG_NAME
from
distribution d
left outer join
product p on p.PRODUCT_CODE = d.PRODUCT_CODE
left outer join
fint ft on ft. FIN_ CODE = FIN_ CODE
left outer join
deal as ds On d.SN_ID = ds.SHOP_ID
left outer join
dealersn as dn on d.SN_ID = dn.SN_DATA_ID
left outer join
regionN as rn ON ds.REGION_ID = rn.REGION_ID
where
d.SND_TO_DATE between #dateFrom and #dateTo
and (#prTypeCode is null or p.PR_TYPE_CODE = #prTypeCode)
group by
rn.REG_NAME, ds.REGION_ID, ds.SHOP_ID, d.PRODUCT_CODE, p.PR_NAME,
p.PR_TYPE_CODE, p.PR_ACTIVE, d.FIN_TABLE_CODE, ft.FT_NAME,
ft.FT_ACTIVE, d.SND_TO_DATE,dn.SND_FULLNAME
)
IF (#ZeroPercent = 0 AND #LargeChainsOrTushev = 0)
BEGIN
SELECT DISTINCT *
FROM final
WHERE ZFlag = 0 AND ExFlag = 0
END
ELSE
IF #ZeroPercent = 1 AND #LargeChainsOrTushev = 0
BEGIN
SELECT DISTINCT *
FROM final
WHERE ZFlag = 1 AND ExFlag = 0
END
ELSE IF #ZeroPercent = 0 AND #LargeChainsOrTushev = 1
BEGIN
SELECT DISTINCT *
FROM final
WHERE ZFlag = 0 AND ExFlag = 1
END
ELSE
BEGIN
SELECT DISTINCT *
FROM final
END
You have a with statement that "defines" a bunch of tables, the last of which is called final. This is called a CTE (Common Table Expression). You can use CTEs for many reasons, but the important thing is, you have to use the CTE in a query. Instead you define the CTE and then...do nothing.
If you put a line like this:
select * from final
just after the CTE (before the IF), it will run.
I'm fairly certain I can use a CASE statement in this instance, but I'm clearly not looking at this right.
In my WHERE clause, when a datestamp resides in the month of July, I need to group lump it into my results for the month of August. Here's what I'm trying, but
I'm missing the mark:
and CPTrn_DateTime = case
when datepart(month, CPTrn_DateTime) = 7 then datepart(month, CPTrn_DateTime) in (7,8)
The reason I was trying to use a CASE statement is because only for the month of July do I need those results to be associated with August results. The reason I can't do a simple date range is because I need the logic to work for all dates moving forward as time goes on.
Here's the entire query:
DECLARE #month INT
SET #month = 8
SELECT DISTINCT cp.CPTrn_Key
,ch.Chg_Ref_No
,c.Cust_Alias
,ch.Chg_Total_Units
,ch.Chg_Amount
,cp.CPTrn_DateTime
FROM PDICompany_2049_01..CP_Transactions cp(NOLOCK)
INNER JOIN PDICompany_2049_01..Customers c(NOLOCK) ON CPTrn_Cust_Key = Cust_Key
INNER JOIN PDICompany_2049_01..Products p(NOLOCK) ON cp.CPTrn_Prod_Key = Prod_Key
LEFT JOIN PDICompany_2049_01..CP_Billing_Details CPBD(NOLOCK) ON CPBd.CPBillDtl_CPTrn_Key = cp.CPTrn_Key
AND CPTrn_Cust_Key = CPBD.CPBillDtl_Cust_Key
AND CPBD.CPBillDtl_Rec_Type = 1
LEFT JOIN PDICompany_2049_01..Customer_Locations cl(NOLOCK) ON c.Cust_WhPrcNtc_Def_CustLoc_Key = cl.CustLoc_Key
AND ((CustLoc_Type & 2) <> 0)
LEFT JOIN Charges ch ON ch.Chg_Ref_No = cpbd.CPBillDtl_Invoice_No
WHERE cp.CPTrn_Tran_Type != 0
AND c.Cust_Alias = 'MONTGOMERY CRANES, LLC'
AND CPBillDtl_Invoice_No IS NOT NULL
AND CPTrn_DateTime = CASE
WHEN datepart(month, CPTrn_DateTime) = 7
THEN datepart(month, CPTrn_DateTime) IN (7,8)
ELSE datepart(month, CPTrn_DateTime) = #month
END AS 'CPTrn_DateTime'
ORDER BY 1
Doing this is a CASE expression (Case statements don't exist in T-SQL) with the DATEPART function is going to likely ruin performance.
You don't "group" in a WHERE, you do that in your GROUP By, so i'm reading through the lines, but what's wrong with "normal" date logic?
WHERE CPTrn_DateTime >= '20190701'
AND CPTrn_DateTime < '20190901'
Edit: Simple date logic is still what you are after:
DECLARE #Month int = 8,
#Year int = 2019;
DECLARE #DateStart date = DATEFROMPARTS(#Year, IIF(#Month = 8, 7, #MOnth),1);
DECLARE #DateEnd date = DATEFROMPARTS(#Year, #Month+1, 1);
SELECT ...
FROM...
WHERE CPTrn_DateTime >= #DateStart
AND CPTrn_DateTime < #DateEnd
Or you can do it all in the query:
SELECT DISTINCT cp.CPTrn_Key
,ch.Chg_Ref_No
,c.Cust_Alias
,ch.Chg_Total_Units
,ch.Chg_Amount
,cp.CPTrn_DateTime
FROM PDICompany_2049_01..CP_Transactions cp(NOLOCK)
INNER JOIN PDICompany_2049_01..Customers c(NOLOCK) ON CPTrn_Cust_Key = Cust_Key
INNER JOIN PDICompany_2049_01..Products p(NOLOCK) ON cp.CPTrn_Prod_Key = Prod_Key
LEFT JOIN PDICompany_2049_01..CP_Billing_Details CPBD(NOLOCK) ON CPBd.CPBillDtl_CPTrn_Key = cp.CPTrn_Key
AND CPTrn_Cust_Key = CPBD.CPBillDtl_Cust_Key
AND CPBD.CPBillDtl_Rec_Type = 1
LEFT JOIN PDICompany_2049_01..Customer_Locations cl(NOLOCK) ON c.Cust_WhPrcNtc_Def_CustLoc_Key = cl.CustLoc_Key
AND ((CustLoc_Type & 2) <> 0)
LEFT JOIN Charges ch ON ch.Chg_Ref_No = cpbd.CPBillDtl_Invoice_No
WHERE cp.CPTrn_Tran_Type != 0
AND c.Cust_Alias = 'MONTGOMERY CRANES, LLC'
AND CPBillDtl_Invoice_No IS NOT NULL
AND #month = case when month(CPTrn_DateTime) = 7 then 8 else month(CPTrn_DateTime) end
I believe order by 1 is superfluous.
I've a following legacy SP:
CREATE PROCEDURE dbo.get_orders_history
(
#FirstDt DATETIME2(6),
#LastDt DATETIME2(6),
#Class VARCHAR(12),
#PeriodType SMALLINT
)
AS
SET NOCOUNT ON
CREATE TABLE #BufferTable (OrderId INT)
INSERT INTO #BufferTable
SELECT DISTINCT
O.OrderId
FROM
BaseOrders O JOIN Classes C ON O.ClassId = C.ClassId
WHERE
(O.Changed = 0) AND
(C.ClassCode = #ClassCode) AND
(
(#PeriodType = 1 AND O.LastActionDateTime >= #FirstDt AND O.OrderDateTime < #LastDt) OR
(#PeriodType = 2 AND O.OrderDateTime >= #FirstDt AND O.OrderDateTime <= #LastDt)
)
OPTION(RECOMPILE);
SELECT A.Column,
C.Column,
OB.Column1,
...
OB.Column10,
O.Column1,
...
O.Column100,
FROM BaseOrders OB
JOIN #BufferTable IDL ON (OB.OrderId = IDL.OrderId)
JOIN Orders O ON (O.OrderId = IDL.OrderId)
JOIN Classes C ON (O.ClassId = C.ClassId)
ORDER BY
O.OrderId
DROP TABLE #BufferTable
GO
The parameter 'PeriodType' is to be added now and I doubt whether this way of make branch (condition in WHERE-clause) is efficient.
SP is rarely called but returns a lot of rows (100K+), so I think OPTION RECOMPILE for SELECT is reasonable solution in this case.
Could any of SQL experts suggest more efficient way to implement such branch?
--
EDIT: I will clarify that current prod version of SP has no parameter 'Period type' and WHERE is following:
WHERE
(O.Changed = 0) AND
(C.ClassCode = #ClassCode) AND
(O.LastActionDateTime >= #FirstDt AND O.OrderDateTime < #LastDt)
My goal is to implement two types of date range type in current SP without or with minimal performance penalties.
Did you try removing OR from the initial insert and deleting afterwards with negation? Here is a code snippet.
CREATE TABLE #BufferTable (OrderId INT, LastActionDateTime datetime, OrderDateTime datetime)
INSERT INTO #BufferTable
SELECT DISTINCT
o.OrderId
, o.LastActionDateTime
, o.OrderDateTime
FROM
BaseOrders O JOIN Classes C ON O.ClassId = C.ClassId
WHERE
(O.Changed = 0) AND
(C.ClassCode = #ClassCode);
delete p
from #BufferTable p
where not
(
(#PeriodType = 1 AND p.LastActionDateTime >= #FirstDt AND p.OrderDateTime < #LastDt) OR
(#PeriodType = 2 AND p.OrderDateTime >= #FirstDt AND p.OrderDateTime <= #LastDt)
)
I have the following scenario in my SQL query. I have to use ROW_NUMBER() in my query, where some aggregate functions also are used to get the result.
SELECT
#TotalRequests = ReportCount.TotalCount,
#TotalTimeToRespond = ReportCount.TotalTimeToRespond,
#TotalRequestsHavingQnA = ReportCount.TotalRequestsHavingQnA,
#ResponseCompliance = ReportCount.ResponseCompliance,
#TotalSubmissions = ReportCount.TotalSubmissions
FROM
(SELECT
TotalCount = Count(1),
TotalTimeToRespond = SUM(Datediff(DAY, DCR.DateReceivedInCB, DCR.DueDate)),
TotalRequestsHavingQnA = SUM(CASE
WHEN DCR.NoOfQuestionsAsked IS NULL
OR DCR.NoOfQuestionsAsked = 0 THEN 0
ELSE 1
END),
ResponseCompliance = Sum(CASE
WHEN DCR.NoOfQuestionsAsked IS NULL
OR DCR.NoOfQuestionsAsked = 0 THEN 0
ELSE ( Cast(DCR.NoOfQuestionsAnswered AS DECIMAL) * 100 ) / Cast(DCR.NoOfQuestionsAsked AS DECIMAL)
END),
TotalSubmissions = Sum(CASE
WHEN DCR.DateSubmitted IS NOT NULL THEN 1
ELSE 0
END),
rowNumber = Row_number()
OVER (
PARTITION BY DCR.callref
ORDER BY DCR.DateSubmitted DESC)
FROM
DimCBComparisonReport DCR
INNER JOIN
DimClientLoc DCL ON DCR.ClientLocKey = DCL.ClientLocKey
WHERE
Ltrim(Rtrim(DCL.LocId)) IN (SELECT PARAM
FROM Fn_splitparam(#LocationIdList, ','))
AND (CASE
WHEN #EnabledDateType = 'D' THEN DCR.DueDate
ELSE DCR.DateSubmitted
END) BETWEEN #StartDate AND #EndDate
AND DCL.ContractId = #ContractRef
AND DCR.EmployeeKey IS NOT NULL) ReportCount
WHERE
ReportCount.rowNumber = 1
So here I am getting an exception like :
Msg 8120, Level 16, State 1, Procedure etl_CaseBuilder_get_Location_Compliance_Summary, Line 41
Column 'DimCBComparisonReport.CallRef' is invalid in the select list because
it is not contained in either an aggregate function or the GROUP BY
clause.
Any help will be deeply appreciated.
From what I can see, the rest of your columns in that query are aggregate without being windowed functions. I'd change them all to be windowed functions (e.g. sum() over (partition by x) as [Field]) and then change it to a select distinct, see if that works.
Try to the PARTITION BY and ORDER BY in a MAX/MIN. Example below:
Row_number()
OVER (
PARTITION BY MAX(DCR.callref)
ORDER BY MAX(DCR.DateSubmitted) DESC
)
Here's my Count_query:
Declare #yes_count decimal;
Declare #no_count decimal;
set #yes_count=(Select count(*) from Master_Data where Received_Data='Yes');
set #no_count=(Select count(*) from Master_Data where Received_Data='No');
select #yes_count As Yes_Count,#no_count as No_Count,(#yes_count/(#yes_count+#no_count)) As Submission_Count
I am having trouble making joins on these two queries
This is the rest of the query:
Select Distinct D.Member_Id,d.Name,d.Region_Name, D.Domain,e.Goal_Abbreviation,
e.Received_Data, case when Received_Data = 'Service Not Provided' then null
when Received_Data = 'No' then null else e.Improvement end as
Percent_Improvement , case when Received_Data = 'Service Not Provided' then null
when Received_Data = 'No' then null else e.Met_40_20 end as Met_40_20
FROM (
select distinct member_Domains.*,
(case when NoData.Member_Id is null then 'Participating' else ' ' end) as Participating
from
(
select distinct members.Member_Id, members.Name, Members.Region_Name,
case when Domains.Goal_Abbreviation = 'EED Reduction' then 'EED'
When Domains.Goal_Abbreviation = 'Pressure Ulcers' then 'PRU'
when Domains.Goal_Abbreviation = 'Readmissions' then 'READ' else Domains.Goal_Abbreviation end as Domain from
(select g.* from Program_Structure as ps inner join Goal as g on ps.Goal_Id = g.Goal_Id
and ps.Parent_Goal_ID = 0) as Domains
cross join
(select distinct hc.Member_ID, hc.Name,hc.Region_Name from zsheet as z
inner join Hospital_Customers$ as hc on z.CCN = hc.Mcare_Id) as Members
) as member_Domains
left outer join Z_Values_Hospitals as NoData on member_Domains.member_ID = NoData.Member_Id
and Member_Domains.Domain = noData.ReportName) D
Left Outer JOIN
(SELECT B.Member_ID, B.Goal_Abbreviation, B.minRate, C.maxRate, B.BLine, C.Curr_Quarter, B.S_Domain,
(CASE WHEN B.Member_ID IN
(SELECT member_id
FROM Null_Report
WHERE ReportName = B.S_Domain) THEN 'Service Not Provided' WHEN Curr_Quarter = 240 THEN 'Yes' ELSE 'No' END) AS Received_Data,
ROUND((CASE WHEN minRate = 0 AND maxRate = 0 THEN 0 WHEN minRate = 0 AND maxRate > 0 THEN 1 ELSE (((maxRate - minRate) / minRate) * 100) END), .2) AS Improvement,
(CASE WHEN ((CASE WHEN minRate = 0 AND maxRate = 0 THEN 0 WHEN minRate = 0 AND maxRate > 0 THEN 1 ELSE (maxRate - minRate) / minRate END)) <= - 0.4 OR
maxRate = 0 THEN 'Yes' WHEN ((CASE WHEN minRate = 0 AND maxRate = 0 THEN 0 WHEN minRate = 0 AND maxRate > 0 THEN 1 ELSE (maxRate - minRate) / minRate END))
<= - 0.2 OR maxRate = 0 THEN 'Yes' ELSE 'No' END) AS Met_40_20
FROM (SELECT tab.Member_ID, tab.Measure_Value AS minRate, tab.Goal_Abbreviation, A.BLine, tab.S_Domain
FROM Measure_Table_Description AS tab INNER JOIN
(SELECT DISTINCT
Member_ID AS new_memid, Goal_Abbreviation AS new_measure, MIN(Reporting_Period_ID) AS BLine, MAX(Reporting_Period_ID)
AS Curr_Quarter
FROM Measure_Table_Description
WHERE (Member_ID > 1) AND (Measure_Value IS NOT NULL) AND (Measure_ID LIKE '%O%')
GROUP BY Goal_Abbreviation, Member_ID) AS A ON tab.Member_ID = A.new_memid AND tab.Reporting_Period_ID = A.BLine AND
tab.Goal_Abbreviation = A.new_measure) AS B FULL OUTER JOIN
(SELECT tab.Member_ID, tab.Measure_Value AS maxRate, tab.Goal_Abbreviation, A_1.Curr_Quarter
FROM Measure_Table_Description AS tab INNER JOIN
(SELECT DISTINCT
Member_ID AS new_memid, Goal_Abbreviation AS new_measure,
MIN(Reporting_Period_ID) AS BLine, MAX(Reporting_Period_ID)
AS Curr_Quarter
FROM Measure_Table_Description AS Measure_Table_Description_1
WHERE (Member_ID >1) AND (Measure_Value IS NOT NULL) AND (Measure_ID LIKE '%O%')
GROUP BY Goal_Abbreviation, Member_ID) AS A_1 ON tab.Member_ID = A_1.new_memid
AND tab.Reporting_Period_ID = A_1.Curr_Quarter AND
tab.Goal_Abbreviation = A_1.new_measure) AS C ON B.Member_ID = C.Member_ID
WHERE (B.Goal_Abbreviation = C.Goal_Abbreviation) ) E ON D.Member_Id = E.Member_ID AND d.Domain = E.S_Domain
ORDER BY D.Domain,D.Member_ID
How do I get a count of the 'yes'/ (count(yes)+count(no)) for each member_ID as column1 and also display the rank of each member_ID against all the member_IDs in the result as column2. I have come up with a query that generates the count for the entire table, but how do I restrict it each Member_ID.
Thanks for your help.
I haven't taken the time to digest your provided query, but if abstracted to the concept of having an aggregate over a range of data repeated on each row, you should look at using windowing functions. There are other methods, such as using a CTE to do your aggregation and then JOINing back to your detailed data. That might work better for more complex calculations, but the window functions are arguably the more elegant option.
DECLARE #MasterData AS TABLE
(
MemberID varchar(50),
MemberAnswer int
);
INSERT INTO #MasterData (MemberID, MemberAnswer) VALUES ('Jim', 1);
INSERT INTO #MasterData (MemberID, MemberAnswer) VALUES ('Jim', 0);
INSERT INTO #MasterData (MemberID, MemberAnswer) VALUES ('Jim', 1);
INSERT INTO #MasterData (MemberID, MemberAnswer) VALUES ('Jim', 1);
INSERT INTO #MasterData (MemberID, MemberAnswer) VALUES ('Jane', 1);
INSERT INTO #MasterData (MemberID, MemberAnswer) VALUES ('Jane', 0);
INSERT INTO #MasterData (MemberID, MemberAnswer) VALUES ('Jane', 1);
-- Method 1, using windowing functions (preferred for performance and syntactical compactness)
SELECT
MemberID,
MemberAnswer,
CONVERT(numeric(19,4),SUM(MemberAnswer) OVER (PARTITION BY MemberID)) / CONVERT(numeric(19,4),COUNT(MemberAnswer) OVER (PARTITION BY MemberID)) AS PercentYes
FROM #MasterData;
-- Method 2, using a CTE
WITH MemberSummary AS
(
SELECT
MemberID,
SUM(MemberAnswer) AS MemberYes,
COUNT(MemberAnswer) AS MemberTotal
FROM #MasterData
GROUP BY MemberID
)
SELECT
md.MemberID,
md.MemberAnswer,
CONVERT(numeric(19,4),MemberYes) / CONVERT(numeric(19,4),MemberTotal) AS PercentYes
FROM #MasterData md
JOIN MemberSummary ms
ON md.MemberID = ms.MemberID;
First thought is: your query is much, much too complicated. I have spent about 10 minutes now trying to make sense of it and haven't gotten anywhere, so it's obviously going to pose a long-term maintenance challenge to those within your organization going forward as well. I would really recommend you try to find some way of simplifying it.
That said, here is a simplified, general example of how to query on a calculated value and rank the results:
CREATE TABLE member (member_id INT PRIMARY KEY);
CREATE TABLE master_data (
transaction_id INT PRIMARY KEY,
member_id INT FOREIGN KEY REFERENCES member(member_id),
received_data BIT
);
-- INSERT data here
; WITH member_data_counts AS (
SELECT
m.member_id,
(SELECT COUNT(*) FROM master_data d WHERE d.member_id = m.member_id AND d.received_data = 1) num_yes,
(SELECT COUNT(*) FROM master_data d WHERE d.member_id = m.member_id AND d.received_data = 0) num_no
FROM member m
), member_data_calc AS (
SELECT
*,
CASE
WHEN (num_yes + num_no) = 0 THEN NULL -- avoid division-by-zero error
ELSE num_yes / (num_yes + num_no)
END pct_yes
FROM member_data_counts
), member_data_rank AS (
SELECT *, RANK() OVER (ORDER BY pct_yes DESC) AS RankValue
FROM member_data_calc
)
SELECT *
FROM member_data_rank
ORDER BY RankValue ASC;