在SQL数据透视查询中用默认值替换行中的空值

时间:2018-10-10 14:16:43

标签: sql-server

使用SQL Server 2016数据库,我使用以下代码:

SELECT * FROM (
    SELECT
        u.firstname AS 'first name',
        u.lastname AS 'last name',
        gi.idnumber AS 'examcode',
        gg.finalgrade AS 'grade'
    FROM mdl_grade_grades gg
    INNER JOIN mdl_grade_items gi ON gg.itemid = gi.id
    INNER JOIN mdl_user u ON gg.userid = u.id
    WHERE gi.idnumber IN ('148','414','413','228','359','379','398','104','351','436','434','384','385','377','280','395')
    AND gg.userid = '62750'
) SOURCE
PIVOT (
    MAX(grade)
    FOR examcode IN ([148],[414],[413],[228],[359],[379],[398],[104],[351],[436],[434],[384],[385],[377],[280],[395])
) PIVT
ORDER BY 'last name', 'first name'

生成各种gi.examcodes的分数的枢轴列表。 如果没有特定gi.examcode的记录(行),则SQL Server数据库将返回NULL,如下所示:

first name  last name    148    413    228    359    379    398     104    351    436    434    384    385    377    280    395
John        Brown        94     96     97     NULL   NULL   NULL    100    NULL   NULL   94     86     83     85     93     NULL

当写入html页面时,该表将在有NULL的地方显示空值。我该如何用通用值替换返回的NULL,如下所示:

first name  last name    148    413    228    359    379    398     104    351    436    434    384    385    377    280    395
John        Brown        94     96     97     None   None   None    100    None   None   94     86     83     85     93     None

4 个答案:

答案 0 :(得分:2)

最好在表示层处理此类表示问题。但是,如果不能,则必须更改查询,因为PIVOT不直接支持表达式。一些选项:

选项1-嵌套PIVOT

SELECT [first name], [last name], 
  [148] = COALESCE([148],'None'), [414] = COALESCE([414],'None'),
  [413] = COALESCE([413],'None'), [228] = COALESCE([228],'None'),
  [359] = COALESCE([359],'None'), [379] = COALESCE([379],'None'),
  [398] = COALESCE([398],'None'), [104] = COALESCE([104],'None'),
  [351] = COALESCE([351],'None'), [436] = COALESCE([436],'None'),
  [434] = COALESCE([434],'None'), [384] = COALESCE([384],'None'),
  [385] = COALESCE([385],'None'), [377] = COALESCE([377],'None'),
  [280] = COALESCE([280],'None'), [395] = COALESCE([395],'None')
FROM
(
  SELECT * FROM 
  (
    SELECT
      u.firstname AS [first name],
      u.lastname AS [last name],
      gi.idnumber AS [examcode],
      CONVERT(varchar(11), gg.finalgrade) AS grade -- if grade is actually numeric
    FROM dbo.mdl_grade_grades gg
    INNER JOIN dbo.mdl_grade_items gi ON gg.itemid = gi.id
    INNER JOIN dbo.mdl_user u ON gg.userid = u.id
    WHERE gi.idnumber IN ('148','414','413','228','359','379','398','104',
                          '351','436','434','384','385','377','280','395')
    AND gg.userid = '62750'
  ) SOURCE
  PIVOT (
      MAX(grade)
      FOR examcode IN ([148],[414],[413],[228],[359],[379],[398],[104],
                       [351],[436],[434],[384],[385],[377],[280],[395])
  ) PIVT
) x
ORDER BY [last name], [first name];

选项2-MAX(CASE

SELECT [first name] = firstname, [last name] = lastname,
 [148] = COALESCE(MAX(CASE idnumber WHEN '148' THEN grade END), 'None'),
 [414] = COALESCE(MAX(CASE idnumber WHEN '414' THEN grade END), 'None'),
 [413] = COALESCE(MAX(CASE idnumber WHEN '413' THEN grade END), 'None'),
 [228] = COALESCE(MAX(CASE idnumber WHEN '228' THEN grade END), 'None'),
 [359] = COALESCE(MAX(CASE idnumber WHEN '359' THEN grade END), 'None'),
 [379] = COALESCE(MAX(CASE idnumber WHEN '379' THEN grade END), 'None'),
 [398] = COALESCE(MAX(CASE idnumber WHEN '398' THEN grade END), 'None'),
 [104] = COALESCE(MAX(CASE idnumber WHEN '104' THEN grade END), 'None'),
 [351] = COALESCE(MAX(CASE idnumber WHEN '351' THEN grade END), 'None'),
 [436] = COALESCE(MAX(CASE idnumber WHEN '436' THEN grade END), 'None'),
 [434] = COALESCE(MAX(CASE idnumber WHEN '434' THEN grade END), 'None'),
 [384] = COALESCE(MAX(CASE idnumber WHEN '384' THEN grade END), 'None'),
 [385] = COALESCE(MAX(CASE idnumber WHEN '385' THEN grade END), 'None'),
 [377] = COALESCE(MAX(CASE idnumber WHEN '377' THEN grade END), 'None'),
 [280] = COALESCE(MAX(CASE idnumber WHEN '280' THEN grade END), 'None'),
 [395] = COALESCE(MAX(CASE idnumber WHEN '395' THEN grade END), 'None')
FROM 
(
  SELECT u.firstname, u.lastname, gi.idnumber, 
    grade = CONVERT(varchar(11), gg.finalgrade)
  FROM dbo.mdl_grade_grades AS gg
  INNER JOIN dbo.mdl_grade_items AS gi 
    ON gg.itemid = gi.id
  INNER JOIN dbo.mdl_user AS u 
    ON gg.userid = u.id
  WHERE gi.idnumber IN ('148','414','413','228','359','379','398','104',
                        '351','436','434','384','385','377','280','395')
  AND gg.userid = '62750'
) AS x
GROUP BY lastname, firstname
ORDER BY lastname, firstname;

选项#3-动态SQL-在添加/删除课程方面更加灵活,仅指定一次课程列表,并且根据读者的不同,阅读起来可能更容易/更难。

DECLARE @userid varchar(11) = '62750', @sql nvarchar(max) = N'SELECT 
  [first name] = firstname, 
  [last name]  = lastname';

CREATE TABLE #g(i int IDENTITY(1,1), id varchar(4));

INSERT #g VALUES ('148'),('414'),('413'),('228'),('359'),('379'),('398'),('104'),
                 ('351'),('436'),('434'),('384'),('385'),('377'),('280'),('395');

SELECT @sql += N',
  ' + QUOTENAME(id) + N' = COALESCE(MAX(CASE i WHEN ''' 
  + id + ''' THEN grade END),''None'')'
FROM #g ORDER BY i;

SET @sql += N'
FROM 
(
  SELECT u.firstname, u.lastname, gi.idnumber AS i, 
    grade = CONVERT(varchar(11), gg.finalgrade)
  FROM dbo.mdl_grade_grades AS gg
  INNER JOIN dbo.mdl_grade_items AS gi 
    ON gg.itemid = gi.id
  INNER JOIN dbo.mdl_user AS u 
    ON gg.userid = u.id
  INNER JOIN #g AS g
    ON g.id = gi.idnumber
  WHERE gg.userid = @userid
) AS x
GROUP BY lastname, firstname
ORDER BY lastname, firstname;';

EXEC sys.sp_executesql @sql, N'@userid varchar(11)', @userid;

DROP TABLE #g;

有很多方法可以做到这一点,但是它们都将是丑陋的或不直观的(有时两者都是)。这是因为T-SQL并不是用来美化演示文稿的-这就是HTML,CSS,JavaScript和其他客户端技术的目的。另外,您应该将数字存储为数字并将其视为数字。还有always use semi-colonsalways use the schema prefix,并且不要使用AS 'last name'-相反,使用AS [last name]更安全。

答案 1 :(得分:1)

而不是在外部选择中使用splat,而是对每个字段进行寻址并处理空值。

SELECT 
    [first name]
    , [last name]
    , [examcode]
    ,isnull([148], 0) as [148]
    ,isnull([414], 0) as [414]
    ,isnull([413], 0) as [413]
    ,isnull([228], 0) as [228]
    ,isnull([359], 0) as [359]
    ,isnull([379], 0) as [379]
    ,isnull([398], 0) as [398]
    ,isnull([104], 0) as [104]
    ,isnull([351], 0) as [351]
    ,isnull([436], 0) as [436]
    ,isnull([434], 0) as [434]
    ,isnull([384], 0) as [384]
    ,isnull([385], 0) as [385]
    ,isnull([377], 0) as [377]
    ,isnull([280], 0) as [280]
    ,isnull([395], 0) as [395]
 FROM (
    SELECT
        u.firstname AS 'first name',
        u.lastname AS 'last name',
        gi.idnumber AS 'examcode',
        gg.finalgrade AS 'grade'
    FROM mdl_grade_grades gg
    INNER JOIN mdl_grade_items gi ON gg.itemid = gi.id
    INNER JOIN mdl_user u ON gg.userid = u.id
    WHERE gi.idnumber IN ('148','414','413','228','359','379','398','104','351','436','434','384','385','377','280','395')
    AND gg.userid = '62750'
) SOURCE
PIVOT (
    MAX(grade)
    FOR examcode IN ([148],[414],[413],[228],[359],[379],[398],[104],[351],[436],[434],[384],[385],[377],[280],[395])
) PIVT
ORDER BY 'last name', 'first name'

答案 2 :(得分:1)

另一种选择是通过CROSS JOIN创建可能组合的子集,然后在PIVOT中执行UNION ALL

示例

;with cte0 as (
    SELECT
        u.firstname AS [first name],
        u.lastname AS [last name],
        gi.idnumber AS [examcode],
        convert(varchar(50),gg.finalgrade) AS [grade]   
    FROM mdl_grade_grades gg
    INNER JOIN mdl_grade_items gi ON gg.itemid = gi.id
    INNER JOIN mdl_user u ON gg.userid = u.id
    WHERE gi.idnumber IN ('148','414','413','228','359','379','398','104','351','436','434','384','385','377','280','395')
    AND gg.userid = '62750'
),cte1 as (
    Select [first name],[last name],examcode,grade
     From (Select Distinct [first name],[last name] from cte0 ) A
     Cross Join (Select Distinct examcode,grade='None' from cte0) B
)
Select *
 From (
        Select * from cte0
        Union All
        Select * from cte1
      ) src
 Pivot (
        max(grade)
        for examcode IN ([148],[414],[413],[228],[359],[379],[398],[104],[351],[436],[434],[384],[385],[377],[280],[395])
       ) pvt
 ORDER BY [last name], [first name]

答案 3 :(得分:0)

您可以将当前查询设为CTE或派生表,然后使用ISNULL()从中进行选择。