2017-10-12 61 views
0

请耐心等待,我是一名SQL初学者,现在我一直被困在一个问题上几个小时。我经常遇到的每个小问题都已经在论坛上回答过了,所以我没有任何理由可以发帖。参数化查询返回错误的数据类型

我目前在统计仪表板编写查询的过程中,打在这一个墙:

OmbudID和GRUPP是整数 Födelsedatum和Startdatum是日期

不使用UNION的查询工作很好,参数@start和@slut的工作方式与TimePicker给出的值一样,但只要我使用UNION,Query似乎会将参数与期望值一起转换为varchar(3)。我试图CAST Startdatum AS Date,但只给了我预期的值varchar(1024)。

我已经尝试了多种不同的解决方案,无济于事,所以我转向你们。

预先感谢您!

SELECT '18-27' as Caption, count(*) as Count 
FROM clients 
WHERE DATEDIFF (year, clients.Födelsedatum, clients.Startdatum) < 28 
AND ((clients.OmbudID = @ombud) OR (COALESCE (@ombud, '') = '')) 
AND ((clients.Grupp = @grupp) OR (COALESCE (@grupp, '') = '')) 
AND clients.Startdatum BETWEEN @start AND @slut 
union all 

SELECT '28-37' as Caption, count(*) as Count 
FROM clients 
WHERE DATEDIFF (year, clients.Födelsedatum, clients.Startdatum) >= 28 
AND DATEDIFF (year, clients.Födelsedatum, clients.Startdatum) <= 37 
AND ((clients.OmbudID = @ombud) OR (COALESCE (@ombud, '') = '')) 
AND ((clients.Grupp = @grupp) OR (COALESCE (@grupp, '') = '')) 
union all 

SELECT '38-47' as Caption, count(*) as Count 
FROM clients 
WHERE DATEDIFF (year, clients.Födelsedatum, clients.Startdatum) >= 38 
AND DATEDIFF (year, clients.Födelsedatum, clients.Startdatum) <= 47 
AND ((clients.OmbudID = @ombud) OR (COALESCE (@ombud, '') = '')) 
AND ((clients.Grupp = @grupp) OR (COALESCE (@grupp, '') = '')) 
union all 

SELECT '48-57' as Caption, count(*) as Count 
FROM clients 
WHERE DATEDIFF (year, clients.Födelsedatum, clients.Startdatum) >= 48 
AND DATEDIFF (year, clients.Födelsedatum, clients.Startdatum) <= 57 
AND ((clients.OmbudID = @ombud) OR (COALESCE (@ombud, '') = '')) 
AND ((clients.Grupp = @grupp) OR (COALESCE (@grupp, '') = '')) 
union all 

SELECT '58-' as Caption, count(*) as Count 
FROM clients 
WHERE DATEDIFF (year, clients.Födelsedatum, clients.Startdatum) >= 58 
AND ((clients.OmbudID = @ombud) OR (COALESCE (@ombud, '') = '')) 
AND ((clients.Grupp = @grupp) OR (COALESCE (@grupp, '') = '')) 

编辑:表定义

CREATE TABLE [dbo].[clients] (
[KlientID]       INT   IDENTITY (1, 1) NOT NULL, 
[Förnamn]       VARCHAR (25) NULL, 
[Efternamn]       VARCHAR (25) NULL, 
[Födelsedatum]      DATE   NULL, 
[OmbudID]       INT   NULL, 
[Grupp]        TINYINT  NULL, 
[Startdatum]       DATE   NULL, 
[Slutdatum]       DATE   NULL, 
PRIMARY KEY CLUSTERED ([KlientID] ASC) 
+0

您的查询不会改变您的参数的数据类型。但是我们在这里做得不多,因为你提供的只是一个查询。没有参数定义,没有表定义。说实话,这可以重写为一个单一的查询与一些聚合和分组。 –

回答

0

敢肯定你可以简化这个查询到这样的事情。但仍然不知道你面临的问题是什么。

SELECT 
    Caption = case 
     when DATEDIFF (year, c.Födelsedatum, c.Startdatum) < 28 AND c.Startdatum BETWEEN @start AND @slut 
      then '18-27' 
     WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 28 AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 37 
      then '28-37' 
     WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 38 AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 47 
      then '38-47' 
     WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 48 AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 57 
      then '48-57' 
     WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 58 
      then '58-' 
    end 
    , count(*) as Count 
FROM clients c 
WHERE 
(
    c.OmbudID = @ombud 
    OR 
    COALESCE (@ombud, '') = '' 
) 
AND 
(
    c.Grupp = @grupp 
    OR 
    COALESCE (@grupp, '') = '' 
) 

group by case when DATEDIFF (year, c.Födelsedatum, c.Startdatum) < 28 
     then '18-27' 
    WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 28 
     AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 37 
     then '28-37' 
    WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 38 
     AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 47 
     then '38-47' 
    WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 48 
     AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 57 
     then '48-57' 
    WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 58 
     then '58-' 
    end 
0

不知道参数类型是怎么回事,但我可以帮助查询。有几件事你可以做多次绕过整个表的UNION结果。

你可以这样做:

WITH Bands As (
    SELECT 18 "Low", 27 "High" 
    Union 
    SELECT 28, 37 
    Union 
    SELECT 38, 47 
    Union 
    SELECT 48, 57 
    Union 
    SELECT 58, NULL 
) 
SELECT cast(b.Low as varchar(2)) + '-' + coalesce(cast(b.High as varchar(2)),'') "Caption", 
     count(*) "Count" 
FROM Bands b 
INNER JOIN client c ON 
     DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= b.Low 
    AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= coalesce(b.High, 9999) 
WHERE c.OmbudID = COALESCE(@ombud, c.OmbudID) 
    AND c.Grupp = COALESCE(@grupp, c.Grupp) 
    AND c.Startdatum BETWEEN @start AND @slut 
GROUP BY b.Low, b.High 

我确实还包括UNION,而不是在你的主表。我喜欢这个选项,因为它可以让你有这个数据驱动。以后很容易改变你需要的乐队,如果你愿意,你可以把它放在真正的桌子上,而不是CTE,这意味着联盟并不是真的有必要。

但它不是唯一的选择。你也可以试试这个:

SELECT 
     CASE WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 27 
       THEN '18-27' 
      WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 28 
      AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 37 
       THEN '28-37' 
      WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 28 
      AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 47 
       THEN '38-47' 
      WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 48 
      AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 57 
       THEN '48-57' 
      WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 58 
       THEN '58-' END "Caption", 
      COUNT(*) "Count" 
FROM clients c 
WHERE c.OmbudID = COALESCE(@ombud, c.OmbudID) 
    AND c.Grupp = COALESCE(@grupp, c.Grupp) 
    AND c.Startdatum BETWEEN @start AND @slut 
GROUP BY 
    CASE WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 27 
       THEN '18-27' 
      WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 28 
      AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 37 
       THEN '28-37' 
      WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 28 
      AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 47 
       THEN '38-47' 
      WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 48 
      AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 57 
       THEN '48-57' 
      WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 58 
       THEN '58-' END  

但是这里的代码重复使我困扰。我们可以因子它是这样的:

SELECT Caption, Count(*) "Count" 
FROM (
    SELECT CASE WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 27 
        THEN '18-27' 
       WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 28 
       AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 37 
        THEN '28-37' 
       WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 28 
       AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 47 
        THEN '38-47' 
       WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 48 
       AND DATEDIFF (year, c.Födelsedatum, c.Startdatum) <= 57 
        THEN '48-57' 
       WHEN DATEDIFF (year, c.Födelsedatum, c.Startdatum) >= 58 
        THEN '58-' END "Caption" 
    FROM Clients c 
    WHERE c.OmbudID = COALESCE(@ombud, c.OmbudID) 
     AND c.Grupp = COALESCE(@grupp, c.Grupp) 
     AND c.Startdatum BETWEEN @start AND @slut 
) Ages 
GROUP BY Caption 

只是警告说,在SQL土地,它往往是必要的性能重复代码,即使它似乎从维护的角度来看繁琐写的和危险的。

我这样说,因为是分解出更多的代码最后一个选项:

SELECT Caption, Count(*) "Count" 
FROM 
(
    SELECT CASE WHEN Age <= 27 THEN '18-27' 
       WHEN Age BETWEEN 28 AND 37 THEN '28-37' 
       WHEN Age BETWEEN 38 AND 47 THEN '38-47' 
       WHEN Age BETWEEN 48 AND 57 THEN '48-57' 
       ELSE '58-' END "Caption" 
    FROM (
     SELECT DATEDIFF (year, c.Födelsedatum, c.Startdatum) "Age" 
     FROM Clients c 
     WHERE c.OmbudID = COALESCE(@ombud, c.OmbudID) 
      AND c.Grupp = COALESCE(@grupp, c.Grupp) 
      AND c.Startdatum BETWEEN @start AND @slut 
    ) Ages 
) Bands 
GROUP BY Caption 

对于嵌套查询,你由内而外的读取。这首先将年龄因素考虑在内,然后在CASE声明中使用该年龄段来获得年龄段,然后通过年龄段乐队进行分组以获得标题和数量。这很有吸引力,因为你只需要一个DATEDIFF()CASE陈述是高度可读的。嵌套查询可能会引起混淆,但可以通过CTE提高可读性和可维护性:

WITH ClientAges AS (
    SELECT DATEDIFF (year, c.Födelsedatum, c.Startdatum) "Age" 
    FROM Clients c 
    WHERE c.OmbudID = COALESCE(@ombud, c.OmbudID) 
     AND c.Grupp = COALESCE(@grupp, c.Grupp) 
     AND c.Startdatum BETWEEN @start AND @slut 
), AgeBands As (
    SELECT CASE WHEN Age <= 27 THEN '18-27' 
       WHEN Age BETWEEN 28 AND 37 THEN '28-37' 
       WHEN Age BETWEEN 38 AND 47 THEN '38-47' 
       WHEN Age BETWEEN 48 AND 57 THEN '48-57' 
       ELSE '58-' END "Caption" 
    FROM ClientAges 
) 
SELECT Caption, COUNT(*) "Count" 
FROM AgeBands 
GROUP BY Caption 

但是。如果Sql Server从这段代码中产生了一个不太理想的执行计划,我不会感到惊讶。它必须为每个记录生成一个文本字符串,而不仅仅是按照每个组分组记录和生成一次文本。您需要针对其他每个人查看该选项,以了解他们的工作方式。

最后,看看您的原始代码,我很好奇你是否希望Startdatum BETWEEN @start AND @slut条件仅适用于<=27频段或所有频段。我一直认为这是其他乐队的疏忽,但如果我错了,这是一个简单的调整,我可以让你留下评论的效果。

+0

谢谢!我还没有时间去尝试新的查询。 “@start”和“@slut”应该适用于所有乐队,我只是在试图找出什么是错误的时候将它遗漏了。 –