<think>根据错误信息,问题出在`CombinedData` CTE中引用了`A.预收款得分`和`A.预收款金额`,但在`CombinedData`的SELECT子句中并没有从`#AdvanceScore`别名A中选取这些列。实际上,在`CombinedData`中,我们使用了`FULL OUTER JOIN`,但可能某些表没有这些列。
仔细检查,在`CombinedData`中,我们使用了四个表:`#ProfitScore`(别名P)、`#AdvanceScore`(别名A)、`#OverdueScore`(别名O)和`#InventoryScore`(别名I)。每个表都有自己的列,但我们在`CombinedData`的SELECT子句中使用了以下列:
P: 总毛利, 毛利得分
A: 预收款金额, 预收款得分
O: 超期应收款, 超期得分
I: 库存金额, 库存得分
但是,在`CombinedData`的SELECT子句中,我们直接使用了这些列,并且使用了别名(A.预收款金额等)。然而,在`CombinedData`中,我们使用了`COALESCE`来合并客户编号和客户名称,但其他列没有合并,而是直接取自某个表。这样,当某个表没有该客户时,对应的列就会为NULL。
错误信息表明在`CombinedData`中无法绑定`A.预收款得分`和`A.预收款金额`,可能是因为在`CombinedData`的SELECT子句中没有正确引用这些列。但是,我们确实在`CombinedData`中引用了这些列。
实际上,这个错误可能是由于在`FinalDisplay`的CTE中引用了`CombinedData`中的列,但在`CombinedData`中并没有这些列?不,我们在`CombinedData`中已经选择了这些列。
重新检查代码,发现错误信息是在执行时出现的,说明在`CombinedData`中引用了`A.预收款得分`,但可能由于`#AdvanceScore`临时表没有成功创建,或者列名不匹配。
但是,我们注意到在`CombinedData`的SELECT子句中,我们使用了`A.预收款得分`,而`#AdvanceScore`中确实有这些列。
另一种可能是,在`CombinedData`的SELECT子句中,我们使用了别名,但可能因为`FULL OUTER JOIN`导致某些行为空,但列名应该存在。
然而,错误信息是编译时错误,说明在解析查询时,无法识别这些列。这可能是由于列名在临时表中不存在,或者临时表创建失败。
但是,我们注意到在`#AdvanceScore`的创建语句中,列名是`预收款得分`和`预收款金额`,而在`CombinedData`中我们使用了`A.预收款得分`,所以列名应该匹配。
考虑到可能是列名中文字符的问题,我们可以尝试用方括号括起来,例如:`A.[预收款得分]`。
但更可能的原因是,在`CombinedData`的SELECT子句中,我们使用了`A.预收款得分`,但如果没有匹配的行,那么`A`整个就是NULL,所以`A.预收款得分`就会无效。在SQL Server中,当使用`FULL JOIN`时,如果某个表没有匹配,那么该表的所有列都为NULL,但列名仍然可以引用。所以这个错误不应该发生。
因此,我们需要检查是否在`#AdvanceScore`中确实有`预收款得分`和`预收款金额`列。
由于错误信息是“无法绑定”,可能是列名写错了。检查`#AdvanceScore`的创建:
```sql
SELECT
A.客户编号,
A.客户名称,
A.预收款金额,
(ISNULL(占比得分, 0) + ISNULL(金额得分, 0)) / 2 * 0.2 AS 预收款得分
INTO #AdvanceScore
...
```
这里,我们创建了`预收款金额`和`预收款得分`两列。所以列名是正确的。
那么,为什么在`CombinedData`中会报错呢?可能是由于`CombinedData`中引用的列名在临时表创建之后被修改了?或者有同名的临时表覆盖?
我们注意到在`CombinedData`之前,我们删除了`#AdvanceScore`(在`#AdvanceScore`的创建语句中使用了`IF OBJECT_ID('tempdb..#AdvanceScore') IS NOT NULL DROP TABLE #AdvanceScore;`),所以应该是重新创建了。
因此,我们考虑可能是列名在`CombinedData`中由于别名使用的问题。在`CombinedData`中,我们这样写:
```sql
;WITH CombinedData AS (
SELECT
COALESCE(P.客户编号, A.客户编号, O.客户编号, I.客户编号) AS 客户编号,
COALESCE(P.客户名称, A.客户名称, O.客户名称, I.客户名称) AS 客户名称,
P.总毛利,
P.毛利得分,
A.预收款金额,
A.预收款得分,
O.超期应收款,
O.超期得分,
I.库存金额,
I.库存得分
FROM #ProfitScore P
FULL OUTER JOIN #AdvanceScore A ON P.客户编号 = A.客户编号
FULL OUTER JOIN #OverdueScore O ON P.客户编号 = O.客户编号
FULL OUTER JOIN #InventoryScore I ON P.客户编号 = I.客户编号
)
```
这里,我们直接引用了`A.预收款金额`和`A.预收款得分`。如果`#AdvanceScore`临时表没有创建成功,或者列名在创建时使用了不同的名称(比如有空格等),则可能导致问题。
但是,我们检查到在`#AdvanceScore`的创建中,列名是“预收款金额”和“预收款得分”,没有空格。
因此,我们尝试用方括号将列名括起来,避免因列名是中文而可能引起的解析问题。修改为:
```sql
;WITH CombinedData AS (
SELECT
COALESCE(P.客户编号, A.客户编号, O.客户编号, I.客户编号) AS 客户编号,
COALESCE(P.客户名称, A.客户名称, O.客户名称, I.客户名称) AS 客户名称,
P.总毛利,
P.毛利得分,
A.[预收款金额],
A.[预收款得分],
O.超期应收款,
O.超期得分,
I.库存金额,
I.库存得分
FROM #ProfitScore P
FULL OUTER JOIN #AdvanceScore A ON P.客户编号 = A.客户编号
FULL OUTER JOIN #OverdueScore O ON P.客户编号 = O.客户编号
FULL OUTER JOIN #InventoryScore I ON P.客户编号 = I.客户编号
)
```
同时,在后面的`FinalDisplay`中,我们也要使用方括号,或者保持原样(因为那里没有报错)。
另外,在`FinalDisplay`中,我们使用了`ISNULL(C.超期得分, 0)`,这里的`C.超期得分`在`CombinedData`中就是`O.超期得分`,所以列名是`超期得分`,同样需要检查。
如果问题仍然存在,我们需要检查是否有其他原因导致临时表`#AdvanceScore`没有被正确创建,比如之前的语句出错导致没有执行到。
为了确保,我们可以在创建`#AdvanceScore`后,添加一个简单的SELECT语句检查,比如:
```sql
SELECT TOP 1 * FROM #AdvanceScore;
```
但在生产环境中,我们可能不希望有额外的输出。因此,我们主要采用加方括号的方式。
修改后的代码片段如下(只修改了CombinedData中的列引用,加上了方括号):
```sql
;WITH CombinedData AS (
SELECT
COALESCE(P.客户编号, A.客户编号, O.客户编号, I.客户编号) AS 客户编号,
COALESCE(P.客户名称, A.客户名称, O.客户名称, I.客户名称) AS 客户名称,
P.总毛利,
P.毛利得分,
A.[预收款金额],
A.[预收款得分],
O.超期应收款,
O.超期得分,
I.库存金额,
I.库存得分
FROM #ProfitScore P
FULL OUTER JOIN #AdvanceScore A ON P.客户编号 = A.客户编号
FULL OUTER JOIN #OverdueScore O ON P.客户编号 = O.客户编号
FULL OUTER JOIN #InventoryScore I ON P.客户编号 = I.客户编号
),
```
同时,在后面的`FinalDisplay`中,我们也要使用方括号,因为那里也引用了这些列:
```sql
FinalDisplay AS (
SELECT
C.客户编号,
DN.客户名称,
ISNULL(C.总毛利, 0) AS 总毛利,
ISNULL(C.毛利得分, 0) AS 毛利得分,
ISNULL(C.[预收款金额], 0) AS 预收款金额,
ISNULL(C.[预收款得分], 0) AS 预收款得分,
ISNULL(OD.超期应收款, 0) AS 超期应收款, -- 展示用
ISNULL(C.超期得分, 0) AS 超期得分,
ISNULL(ID.库存金额, 0) AS 库存金额, -- 展示用
ISNULL(C.库存得分, 0) AS 库存得分
FROM CombinedData C
INNER JOIN DistinctNames DN ON C.客户编号 = DN.客户编号
LEFT JOIN #OverdueForDisplay OD ON C.客户编号 = OD.客户编号
LEFT JOIN #InventoryForDisplay ID ON C.客户编号 = ID.客户编号
)
```
这样修改后,应该可以避免列名解析的问题。
另外,我们还需要注意,在`CombinedData`中,我们使用了四个表的FULL OUTER JOIN,但连接条件都是`P.客户编号 = ...`,这可能导致某些情况下连接失败(比如P中没有该客户编号,而其他表有)。我们使用`COALESCE`来合并客户编号,所以没问题。
但是,如果`#AdvanceScore`中没有`预收款金额`列,那么即使加了方括号也会报错。所以,我们还需要确保`#AdvanceScore`被正确创建。
由于错误信息是“The multi-part identifier "A.预收款得分" could not be bound”,说明在编译查询时,找不到这个列。因此,我们决定在`CombinedData`中不使用别名,而是直接使用列名(因为FULL JOIN后,列名会保留),但用`ISNULL`来合并。但是,这样会有重复列的问题?因为四个表都有`客户编号`和`客户名称`,但其他列名都是唯一的。
所以,我们也可以不用别名,直接写列名(不加表别名),因为列名是唯一的。例如:
```sql
;WITH CombinedData AS (
SELECT
COALESCE(P.客户编号, A.客户编号, O.客户编号, I.客户编号) AS 客户编号,
COALESCE(P.客户名称, A.客户名称, O.客户名称, I.客户名称) AS 客户名称,
P.总毛利,
P.毛利得分,
A.预收款金额, -- 这里不加表别名,直接列名,但这样在多个表有同名列时会出错
A.预收款得分,
O.超期应收款,
O.超期得分,
I.库存金额,
I.库存得分
...
```
但这样写,如果多个表有同名的列,就会报错。但是,在我们的情况下,每个表的列名都是唯一的(除了客户编号和客户名称),所以可以不用表别名。但是,SQL Server要求必须指定表别名或明确列来源。
因此,我们回到使用表别名,并加上方括号。
如果问题仍然存在,我们可以尝试将`CombinedData`中的列名都加上方括号。
修改后的完整代码(仅修改CombinedData和FinalDisplay中的列引用,加上方括号)如下:</think>我理解你遇到了 SQL 绑定错误:"The multi-part identifier 'A.预收款得分' could not be bound"。这个问题通常是由于 SQL 无法识别查询中引用的列名或表别名导致的。让我修复这个问题并提供完整的解决方案。
### 问题分析
错误原因:
1. 在 `CombinedData` CTE 中引用了 `A.预收款得分` 和 `A.预收款金额`
2. 但 `A` 是 `#AdvanceScore` 的别名,而 SQL 无法正确绑定这个别名
3. 可能原因是列名包含中文或特殊字符,或者别名作用域问题
### 修复后的完整代码
```sql
DECLARE @EndDate DATE = GETDATE();
DECLARE @StartDate DATE = DATEADD(DAY, -365, @EndDate);
-- 创建部门临时表
IF OBJECT_ID('tempdb..#TEMP_Department') IS NOT NULL DROP TABLE #TEMP_Department;
CREATE TABLE #TEMP_Department (Department nvarchar(20));
INSERT INTO #TEMP_Department EXEC [ZY_P_Get_SAP_Account_to_TempDept];
-- 基础销售额计算
IF OBJECT_ID('tempdb..#Sales') IS NOT NULL DROP TABLE #Sales;
SELECT
T.客户编号,
MAX(T.客户名称) AS 客户名称,
SUM(T.RMB总价) AS 销售额
INTO #Sales
FROM [ZY_TB_SALES_Details] T
INNER JOIN #TEMP_Department D ON T.综合部门 = D.Department
WHERE T.交货日期 BETWEEN @StartDate AND @EndDate
AND T.业务体系 = '经销体系'
AND (T.客户编号 COLLATE Latin1_General_CI_AS LIKE 'c%')
GROUP BY T.客户编号;
-- 1. 计算总毛利
IF OBJECT_ID('tempdb..#NonProjectProfit') IS NOT NULL DROP TABLE #NonProjectProfit;
SELECT
T.客户编号,
MAX(T.客户名称) AS 客户名称,
SUM(T.扣技服后毛利) AS 非项目毛利
INTO #NonProjectProfit
FROM [ZY_TB_SALES_Details] T
INNER JOIN #TEMP_Department D ON T.综合部门 = D.Department
WHERE T.交货日期 BETWEEN @StartDate AND @EndDate
AND T.业务体系 = '经销体系'
AND T.项目合同YN IS NULL
AND (T.客户编号 COLLATE Latin1_General_CI_AS LIKE 'c%')
GROUP BY T.客户编号;
IF OBJECT_ID('tempdb..#ProjectProfit') IS NOT NULL DROP TABLE #ProjectProfit;
SELECT
客户编号,
MAX(客户名称) AS 客户名称,
SUM(有效综合毛利RMB) AS 项目毛利
INTO #ProjectProfit
FROM ZY_TB_JX_XM_SalesPer_Profit
WHERE 交货日期 BETWEEN @StartDate AND @EndDate
AND 综合部门 IN (SELECT Department FROM #TEMP_Department)
AND (客户编号 COLLATE Latin1_General_CI_AS LIKE 'c%')
GROUP BY 客户编号;
IF OBJECT_ID('tempdb..#ProfitScore') IS NOT NULL DROP TABLE #ProfitScore;
SELECT
COALESCE(N.客户编号, P.客户编号) AS 客户编号,
COALESCE(N.客户名称, P.客户名称) AS 客户名称,
ISNULL(N.非项目毛利, 0) + ISNULL(P.项目毛利, 0) AS 总毛利,
CASE
WHEN ISNULL(N.非项目毛利, 0) + ISNULL(P.项目毛利, 0) >= 1000000 THEN 100
WHEN ISNULL(N.非项目毛利, 0) + ISNULL(P.项目毛利, 0) >= 500000 THEN 80
WHEN ISNULL(N.非项目毛利, 0) + ISNULL(P.项目毛利, 0) >= 100000 THEN 60
WHEN ISNULL(N.非项目毛利, 0) + ISNULL(P.项目毛利, 0) >= 10000 THEN 40
WHEN ISNULL(N.非项目毛利, 0) + ISNULL(P.项目毛利, 0) > 0 THEN 20
ELSE 0
END * 0.3 AS 毛利得分
INTO #ProfitScore
FROM #NonProjectProfit N
FULL OUTER JOIN #ProjectProfit P ON N.客户编号 = P.客户编号;
-- 2. 计算预收款得分
IF OBJECT_ID('tempdb..#Advance') IS NOT NULL DROP TABLE #Advance;
WITH CTE_OACT AS (SELECT AcctCode, AcctName FROM OACT),
CTE_BANK AS (
SELECT DISTINCT Number AS '进账单流水号', BankName AS '银行信息' FROM [WEB_BANKINCOME]
UNION ALL
SELECT 到帐单流水号 AS '进账单流水号', 银行信息 FROM [ZY_TB_WEB_BANKINCOME_OLD]
),
CTE_Result AS (
SELECT
T0.Cardcode AS N'客户编号',
T0.CardName AS N'客户名称',
T0.OpenBal AS N'未清金额',
T0.Canceled
FROM ORCT T0
INNER JOIN CTE_OACT t3 ON t0.TrsfrAcct = t3.AcctCode
LEFT JOIN (SELECT DISTINCT 进账单流水号, 银行信息 FROM CTE_BANK) M0
ON M0.进账单流水号 = T0.U_LSH
LEFT JOIN [ZY_VIEW_XM] X0 ON X0.ConCode = T0.U_contractnumber
LEFT JOIN OCRD d1 ON d1.CardCode = t0.CardCode
WHERE T0.taxdate<= @EndDate
AND T0.U_Department IN ('上海办','生命科学部','分销部','工业部','天津办','基础科研部','沈阳办','烟台办','广州办','成都办','商务部')
AND (T0.Cardcode COLLATE Latin1_General_CI_AS LIKE 'c%')
)
SELECT
[客户编号],
MAX([客户名称]) AS 客户名称,
SUM(CASE WHEN Canceled = 'N' THEN [未清金额] ELSE 0 END) AS 预收款金额
INTO #Advance
FROM CTE_Result
GROUP BY [客户编号];
IF OBJECT_ID('tempdb..#AdvanceScore') IS NOT NULL DROP TABLE #AdvanceScore;
SELECT
客户编号,
客户名称,
预收款金额,
(ISNULL(占比得分, 0) + ISNULL(金额得分, 0)) / 2 * 0.2 AS 预收款得分
INTO #AdvanceScore
FROM #Advance A
LEFT JOIN #Sales S ON A.客户编号 = S.客户编号
CROSS APPLY (
SELECT
CASE
WHEN 预收款金额 / NULLIF(S.销售额, 0) >= 1 THEN 100
WHEN 预收款金额 / NULLIF(S.销售额, 0) >= 0.5 THEN 75
WHEN 预收款金额 / NULLIF(S.销售额, 0) >= 0.1 THEN 50
WHEN 预收款金额 > 0 THEN 25
ELSE 0
END AS 占比得分,
CASE
WHEN 预收款金额 >= 1000000 THEN 100
WHEN 预收款金额 >= 500000 THEN 75
WHEN 预收款金额 >= 100000 THEN 50
WHEN 预收款金额 > 0 THEN 25
ELSE 0
END AS 金额得分
) AS Scores;
-- 3. 计算超期应收款
IF OBJECT_ID('tempdb..#OverdueBase') IS NOT NULL DROP TABLE #OverdueBase;
SELECT
T.客户编码 AS 客户编号,
T.客户名称,
T.未收款金额,
ISNULL(X.XM_YN, 'N') AS 项目合同,
T.DN日期 AS 销货日期,
T.业务体系
INTO #OverdueBase
FROM [ZY_TB_Receivable_Balance_Base] T
INNER JOIN #TEMP_Department X0 ON X0.Department = T.综合部门
LEFT JOIN [ZY_VIEW_XM] X ON X.ConCode = T.合同号
WHERE T.业务体系 = '经销体系'
AND (T.客户编码 COLLATE Latin1_General_CI_AS LIKE 'c%');
-- 展示用:所有非项目应收款
IF OBJECT_ID('tempdb..#OverdueForDisplay') IS NOT NULL DROP TABLE #OverdueForDisplay;
SELECT
客户编号,
MAX(客户名称) AS 客户名称,
SUM(未收款金额) AS 超期应收款
INTO #OverdueForDisplay
FROM #OverdueBase
WHERE 项目合同 = 'N'
GROUP BY 客户编号;
-- 得分用:仅180天以上应收款
IF OBJECT_ID('tempdb..#OverdueForScore') IS NOT NULL DROP TABLE #OverdueForScore;
SELECT
客户编号,
MAX(客户名称) AS 客户名称,
SUM(未收款金额) AS 超期应收款
INTO #OverdueForScore
FROM #OverdueBase
WHERE 项目合同 = 'N'
AND DATEDIFF(DAY, 销货日期, @EndDate) >= 180
GROUP BY 客户编号;
IF OBJECT_ID('tempdb..#OverdueScore') IS NOT NULL DROP TABLE #OverdueScore;
SELECT
客户编号,
客户名称,
超期应收款,
(ISNULL(占比得分, 100) + ISNULL(金额得分, 100)) / 2 * 0.3 AS 超期得分
INTO #OverdueScore
FROM #OverdueForScore O
LEFT JOIN #Sales S ON O.客户编号 = S.客户编号
CROSS APPLY (
SELECT
CASE
WHEN 超期应收款 / NULLIF(S.销售额, 0) >= 1 THEN 0
WHEN 超期应收款 / NULLIF(S.销售额, 0) >= 0.5 THEN 25
WHEN 超期应收款 / NULLIF(S.销售额, 0) >= 0.1 THEN 50
WHEN 超期应收款 > 0 THEN 75
ELSE 100
END AS 占比得分,
CASE
WHEN 超期应收款 >= 1000000 THEN 0
WHEN 超期应收款 >= 500000 THEN 25
WHEN 超期应收款 >= 100000 THEN 50
WHEN 超期应收款 > 0 THEN 75
ELSE 100
END AS 金额得分
) AS Scores;
-- 4. 计算库存
-- 展示用:所有库存
IF OBJECT_ID('tempdb..#InventoryForDisplay') IS NOT NULL DROP TABLE #InventoryForDisplay;
SELECT
[客户编号],
MAX([客户名称]) AS 客户名称,
SUM([含税总价RMB]) AS 库存金额
INTO #InventoryForDisplay
FROM (
SELECT
R0.Cardcode AS '客户编号',
R0.CardName AS '客户名称',
CASE
WHEN A0.U_CustomsRate IS NOT NULL THEN
CASE
WHEN A0.U_Import ='Y' AND A0.U_RevisedDuty IS NOT NULL
THEN (A0.U_ForeignPrice * A0.U_CustomsRate + ISNULL(A0.U_RevisedDuty,0)) * (A0.U_InputVATRate/100+1)
WHEN A0.U_Import ='Y' AND A0.U_RevisedDuty IS NULL
THEN (A0.U_PriceAfVAT + ISNULL(A0.U_Tariff,0)) * (A0.U_InputVATRate/100+1)
ELSE (A0.U_PriceAfVAT + ISNULL(A0.U_Tariff,0))
END * B0.Quantity
ELSE
CASE
WHEN A0.U_Import ='Y'
THEN (A0.U_PriceAfVAT + ISNULL(A0.U_Tariff,0)) * (A0.U_InputVATRate/100+1)
ELSE (A0.U_PriceAfVAT + ISNULL(A0.U_Tariff,0))
END * B0.Quantity
END AS '含税总价RMB'
FROM OBTN A0
JOIN OIBT B0 ON B0.ItemCode = A0.ItemCode
AND B0.BatchNum = A0.DistNumber
AND B0.SysNumber = A0.SysNumber
LEFT JOIN ORDR R0 ON R0.SupplCode = A0.U_SaleContNo
WHERE
B0.Quantity > 0
AND R0.CardName IS NOT NULL
AND A0.U_Department IN ('上海办','生命科学部','分销部','工业部','天津办','基础科研部','沈阳办','烟台办','广州办','成都办','商务部')
AND (R0.Cardcode COLLATE Latin1_General_CI_AS LIKE 'c%')
) AS SubQuery
GROUP BY [客户编号];
-- 得分用:仅60天以上库存
IF OBJECT_ID('tempdb..#InventoryForScore') IS NOT NULL DROP TABLE #InventoryForScore;
SELECT
[客户编号],
MAX([客户名称]) AS 客户名称,
SUM([含税总价RMB]) AS 库存金额
INTO #InventoryForScore
FROM (
SELECT
R0.Cardcode AS '客户编号',
R0.CardName AS '客户名称',
CASE
WHEN A0.U_CustomsRate IS NOT NULL THEN
CASE
WHEN A0.U_Import ='Y' AND A0.U_RevisedDuty IS NOT NULL
THEN (A0.U_ForeignPrice * A0.U_CustomsRate + ISNULL(A0.U_RevisedDuty,0)) * (A0.U_InputVATRate/100+1)
WHEN A0.U_Import ='Y' AND A0.U_RevisedDuty IS NULL
THEN (A0.U_PriceAfVAT + ISNULL(A0.U_Tariff,0)) * (A0.U_InputVATRate/100+1)
ELSE (A0.U_PriceAfVAT + ISNULL(A0.U_Tariff,0))
END * B0.Quantity
ELSE
CASE
WHEN A0.U_Import ='Y'
THEN (A0.U_PriceAfVAT + ISNULL(A0.U_Tariff,0)) * (A0.U_InputVATRate/100+1)
ELSE (A0.U_PriceAfVAT + ISNULL(A0.U_Tariff,0))
END * B0.Quantity
END AS '含税总价RMB'
FROM OBTN A0
JOIN OIBT B0 ON B0.ItemCode = A0.ItemCode
AND B0.BatchNum = A0.DistNumber
AND B0.SysNumber = A0.SysNumber
LEFT JOIN ORDR R0 ON R0.SupplCode = A0.U_SaleContNo
WHERE
B0.Quantity > 0
AND DATEDIFF(DAY, A0.InDate, GETDATE()) >= 60
AND R0.CardName IS NOT NULL
AND A0.U_Department IN ('上海办','生命科学部','分销部','工业部','天津办','基础科研部','沈阳办','烟台办','广州办','成都办','商务部')
AND (R0.Cardcode COLLATE Latin1_General_CI_AS LIKE 'c%')
) AS SubQuery
GROUP BY [客户编号];
IF OBJECT_ID('tempdb..#InventoryScore') IS NOT NULL DROP TABLE #InventoryScore;
SELECT
客户编号,
客户名称,
库存金额,
CASE
WHEN ISNULL(占比得分, 100) < ISNULL(金额得分, 100)
THEN ISNULL(占比得分, 100)
ELSE ISNULL(金额得分, 100)
END * 0.2 AS 库存得分
INTO #InventoryScore
FROM #InventoryForScore I
LEFT JOIN #Sales S ON I.客户编号 = S.客户编号
CROSS APPLY (
SELECT
CASE
WHEN 库存金额 / NULLIF(S.销售额, 0) >= 1 THEN 0
WHEN 库存金额 / NULLIF(S.销售额, 0) >= 0.5 THEN 25
WHEN 库存金额 / NULLIF(S.销售额, 0) >= 0.1 THEN 50
WHEN 库存金额 > 0 THEN 75
ELSE 100
END AS 占比得分,
CASE
WHEN 库存金额 >= 1000000 THEN 0
WHEN 库存金额 >= 500000 THEN 25
WHEN 库存金额 >= 100000 THEN 50
WHEN 库存金额 > 0 THEN 75
ELSE 100
END AS 金额得分
) AS Scores;
-- 5. 汇总最终结果(修复别名绑定问题)
;WITH ScoreData AS (
SELECT
COALESCE(P.客户编号, Adv.客户编号, O.客户编号, Inv.客户编号) AS 客户编号,
COALESCE(P.客户名称, Adv.客户名称, O.客户名称, Inv.客户名称) AS 客户名称,
P.总毛利,
P.毛利得分,
Adv.预收款金额,
Adv.预收款得分,
O.超期得分,
Inv.库存得分
FROM #ProfitScore P
FULL OUTER JOIN #AdvanceScore Adv ON P.客户编号 = Adv.客户编号
FULL OUTER JOIN #OverdueScore O ON P.客户编号 = O.客户编号
FULL OUTER JOIN #InventoryScore Inv ON P.客户编号 = Inv.客户编号
),
DistinctNames AS (
SELECT
客户编号,
STUFF((
SELECT DISTINCT ', ' + tmp.客户名称
FROM ScoreData tmp
WHERE tmp.客户编号 = ScoreData.客户编号
AND tmp.客户名称 IS NOT NULL
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ''
) AS 客户名称
FROM ScoreData
GROUP BY 客户编号
),
FinalScores AS (
SELECT
S.客户编号,
DN.客户名称,
ISNULL(S.总毛利, 0) AS 总毛利,
ISNULL(S.毛利得分, 0) AS 毛利得分,
ISNULL(S.预收款金额, 0) AS 预收款金额,
ISNULL(S.预收款得分, 0) AS 预收款得分,
ISNULL(S.超期得分, 0) AS 超期得分,
ISNULL(S.库存得分, 0) AS 库存得分,
ROUND(
ISNULL(S.毛利得分, 0) +
ISNULL(S.预收款得分, 0) +
ISNULL(S.超期得分, 0) +
ISNULL(S.库存得分, 0),
2) AS 总得分
FROM ScoreData S
INNER JOIN DistinctNames DN ON S.客户编号 = DN.客户编号
)
SELECT
FS.客户编号,
FS.客户名称,
FS.总毛利,
FS.毛利得分,
FS.预收款金额,
FS.预收款得分,
OD.超期应收款, -- 展示用(所有应收款)
FS.超期得分,
ID.库存金额, -- 展示用(所有库存)
FS.库存得分,
FS.总得分
FROM FinalScores FS
LEFT JOIN #OverdueForDisplay OD ON FS.客户编号 = OD.客户编号
LEFT JOIN #InventoryForDisplay ID ON FS.客户编号 = ID.客户编号
ORDER BY FS.总得分 DESC;
-- 清理所有临时表
DROP TABLE #Sales, #NonProjectProfit, #ProjectProfit, #ProfitScore,
#Advance, #AdvanceScore, #TEMP_Department, #OverdueBase,
#OverdueForDisplay, #OverdueForScore, #OverdueScore,
#InventoryForDisplay, #InventoryForScore, #InventoryScore;
```
### 主要修复点
1. **别名绑定问题修复**:
- 将 `CombinedData` 重命名为 `ScoreData`
- 使用更明确的别名:`Adv`(预收款)、`O`(超期)、`Inv`(库存)
- 避免使用单个字母别名,减少冲突可能性
2. **结构优化**:
- 将最终结果拆分为三个CTE:
- `ScoreData`:合并所有得分数据
- `DistinctNames`:处理客户名称合并
- `FinalScores`:计算总得分
- 最后单独连接展示数据(应收款和库存)
3. **列引用修复**:
- 所有列引用都使用明确的表别名
- 避免使用 `A.` 这样可能引起歧义的别名
- 使用 `ISNULL` 处理可能的空值
4. **展示数据处理**:
- 在最终查询中单独连接 `#OverdueForDisplay` 和 `#InventoryForDisplay`
- 保持得分计算逻辑不变,但展示完整数据
### 为什么原始代码会出错
SQL Server 在解析多部分标识符(如 `A.预收款得分`)时:
1. 首先检查 `A` 是否是有效的表/视图别名
2. 然后检查 `预收款得分` 是否是 `A` 的有效列
3. 如果别名作用域不正确或列名包含特殊字符,绑定可能失败
通过使用更明确的别名和重组查询结构,我们避免了这些绑定问题,同时保持了原始业务逻辑不变。