I have a table with the following column, in SQL Server 2012:
ID YEARNBR
-----------
1 2016
2 2015
3 2014
4 2013
1 2015
1 2014
2 2016
I wanted to create a case statement that would check this column YEARNBR:
(case
when yearNBR = 2017 and YearNBR = 2016 and YearNBR = 2015 and yearNBR = 2014
then 1
else 0
end)
If my column YEARNBR would contain the year 2016, 2015, and 2014 and I would be grouping by ID, then I would want another column that would be called: 2014Through2016 that would contain the value 1.
ID 2014Through2016 2015Through2016 2016
--------------------------------------------
1 1 1 1
2 0 1 1
3 0 0 0
4 0 0 0
Any help would be appreciated !
Just use conditional aggregation:
select id,
max(case when yearnbr in (2014, 2015, 2016) then 1 else 0 end) as yr_2014_2016,
max(case when yearnbr = 2016 then 1 else 0 end) as yr_2015
from t
group by id;
create a sameple table:
CREATE TABLE yearz(
id int,
yearnbr int
)
INSERT INTO yearz (id,yearnbr)
VALUES
(1,2016),
(2,2015),
(3,2014),
(4,2013),
(1,2015),
(1,2014),
(2,2016)
using pivot to get your result:
select id, [2014] as '2014through2016', [2015] as '2015through2016', [2016]
from
(
select id, yearnbr
from yearz
) src
pivot
(
count(yearnbr)
for yearnbr in ([2014], [2015], [2016])
) piv;
result:
id 2014through2016 2015through2016 2016
----------- --------------- --------------- -----------
1 1 1 1
2 0 1 1
3 1 0 0
4 0 0 0
Suggestion
You should learn how to use PIVOT and UNPIVOT they are pretty useful. and welcome to StackOverflow. If you find this or any other answer useful please mark it as the solution. That way it would help the community and fellow programmers who run into the same problem as you in the future. Cheers!
select id, sum(yr_2014_2016) as yr_2014_2016, sum(yr_2015_2016) as yr_2015_2016, sum(yr_2016) as yr_2016
from
(
select id
, (case when yearnbr in (2014, 2015, 2016) then 1 else 0 end) as yr_2014_2016
, (case when yearnbr in (2015, 2016) then 1 else 0 end) as yr_2015_2016
, (case when yearnbr = 2016 then 1 else 0 end) as yr_2016
from t;
)
y
group by Id
Related
I have a SQL Server table that has Start (1-1-2017) and End (1-1-2022) dates for contracts with invoices being generated each month for current and past months.
I would like to display months as columns even when no invoice has been generated, is that possible with just SQL / Pivot tables or a table with dates as calendar must be created?
I have worked with this code so far.
WITH CTE_MyTable AS
(
SELECT
FORMAT(MIN(StartDate), 'yyyy-MM') AS [MyDate]
FROM
MyTable
UNION ALL
SELECT
FORMAT(MIN(DATEADD(month, 1, StartDate)), 'yyyy-MM') AS [MyDate]
FROM
MyTable
WHERE
FORMAT(DATEADD(month, 1, StartDate),'yyyy-MM') <= (SELECT FORMAT(MAX(EndDate), 'yyyy-MM') AS [MyDate] FROM MyTable)
)
SELECT [MyDate]
FROM CTE_ MyTable
GROUP BY MyDate
OPTION (MAXRECURSION 0);
So let's say your table looked like this (using temp variable so you can just copy/paste/test):
DECLARE #sale TABLE(saledate DATE, saleamt MONEY);
INSERT #sale VALUES ('20170103',500),('20170128',266),('20170303',4002),('20170409',25);
Note that I'm only doing 6 months for simplicity. The following query get the count of sales per month (first query) and the sum of the sales for the second query:
DECLARE #sale TABLE(saledate DATE, saleamt MONEY);
INSERT #sale VALUES ('20170103',500),('20170128',266),('20170303',4002),('20170409',25);
SELECT
[201701] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 1 THEN 1 END),
[201702] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 2 THEN 1 END),
[201703] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 3 THEN 1 END),
[201704] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 4 THEN 1 END),
[201705] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 5 THEN 1 END),
[201706] = COUNT(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 6 THEN 1 END)
FROM #sale t;
SELECT
[201701] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 1 THEN t.saleamt END),0),
[201702] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 2 THEN t.saleamt END),0),
[201703] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 3 THEN t.saleamt END),0),
[201704] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 4 THEN t.saleamt END),0),
[201705] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 5 THEN t.saleamt END),0),
[201706] = ISNULL(SUM(CASE WHEN YEAR(t.saledate)=2017 AND MONTH(t.saledate) = 6 THEN t.saleamt END),0)
FROM #sale t;
These queries return:
201701 201702 201703 201704 201705 201706
----------- ----------- ----------- ----------- ----------- -----------
2 0 1 1 0 0
201701 201702 201703 201704 201705 201706
--------------------- --------------------- --------------------- --------------------- --------------------- ---------------------
766.00 0.00 4002.00 25.00 0.00 0.00
There is a way to pivot columns in SQL- using the Pivot() function (Microsoft Documentation at: https://learn.microsoft.com/en-us/sql/t-sql/queries/from-using-pivot-and-unpivot?view=sql-server-2017)
To display months as columns (1 to 12) the Pivot() function assigns values to (hard-coded) columns. Implemented correctly, there are NULLS where the aggregation doesn't occur due to lack of records. The implied way to replace NULLS with zeroes is by using the COALESCE() function.
The year values of the pivoted data should be grouped and while there is no specific Group By for a pivot, this is implied by how the SourceTable query is written.
In this example code I use the same exact format as the official documentation; with the exception that I additionally group by year and COALESCE NULLs with 0's.
What I am doing is counting the number of records for a given month and year:
SELECT yearval
,COALESCE([1], 0) [Jan]
,COALESCE([2], 0) [Feb]
,COALESCE([3], 0) [Mar]
,COALESCE([4], 0) [Apr]
,COALESCE([5], 0) [May]
,COALESCE([6], 0) [Jun]
,COALESCE([7], 0) [Jul]
,COALESCE([8], 0) [Aug]
,COALESCE([9], 0) [Sep]
,COALESCE([10], 0) [Oct]
,COALESCE([11], 0) [Nov]
,COALESCE([12], 0) [Dec]
FROM
(SELECT YEAR([Your_Date_Column_Here]) AS [yearval]
,MONTH([Your_Date_Column_Here]) AS [monthval]
FROM [Your_Table_Name_Here]) AS SourceTable
PIVOT
(
COUNT(monthval) FOR monthval IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12])
) AS PivotTable
Would produce this output in my test database:
yearval Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
2015 1952 1122 1678 2364 1125 1308 1414 2103 1031 1340 2506 1015
2016 1123 1413 1568 1421 1278 1252 1048 1290 1251 1571 2647 1253
2017 0 0 0 3 0 1241 2377 2714 6724 1388 1521 1243
2018 2127 2118 2449 2330 2687 3833 3279 883 0 0 0 0
I am new to SQL Server and I am facing this trouble please need your help on this.
I am using SQL Server 2008
I have two tables
table 1: CALENDAR_DIMENSION
Row_Seq(numeric(5,0), null)
CALENDAR_YEAR(int, null)
CALENDAR_MONTH_NO(int, null)
CALENDAR_MONTH_NAME(varchar(15), null)
CALENDAR_DATE(date, null)
table 2: HOLIDAY_DETAILS
CALENDAR_DATE(date, not null)
DESCRIPTION (varchar(50), null)
CALENDAR_DAY_NAME(varchar(15), null)
IS_WORKING_DAY(int, null)
My requirement is to get the data from the two tables as below for the CALENDAR_DATE it should pick the values from HOLIDAY_DETAILS table from the columnn IS_WORKING_DAY (0= Holiday, 1= working day)
Calendar_Year Calendar_Month_No Calendar_Date
2014 1 01-01-2014 02-01-2014 03-01-2014 04-01-2014 05-01-2014 ……. 31-01-2014
2014 2 01-02-2014 02-02-2014 03-02-2013 03-02-2013 04-02-2013 ……. 28-02-2014
2014 3 01-03-2014 02-03-2014 03-03-2014 04-03-2014 05-03-2014 ……. 31-03-2014
Result should be in this manner after Joining to CALENDAR_DIMENSION. First I need all the CALENDAR-YEAR, CALENDAR_MONTH and CALENDAR_DAYS in horizontal ways as shown in the example below from HOLIDAY_DETAILS table and then would like to join to table CALENDAR_DIMENSION to get the IS_WORKING_DAY status on the Days.
Calendar_Year Calendar_Month_No Calendar_Date
2014 1 1 1 1 0 0 ……. 1
2 0 0 1 0 0 ……. 1
3 0 0 1 0 0 ……. 1
Working Day = 1
Holiday = 0
according to me YEAR_MONTH-DAY is useless.i will not create this table .you can verify my script with your requirement and tell how it won't work.after verification you can easily join with holiday table.
Declare #year varchar(4)=2014
;with cte as
(
select #year calyear,1 monthno,cast(#year+'-'+'1'+'-'+'1' as date) as date1,cast(#year+'-'+'1'+'-'+'2' as date) date2,cast(#year+'-'+'1'+'-'+'3' as date) date3,cast(#year+'-'+'1'+'-'+'4' as date) date4,cast(#year+'-'+'1'+'-'+'5' as date) date5,cast(#year+'-'+'1'+'-'+'6' as date) date6,cast(#year+'-'+'1'+'-'+'7' as date) date7
union all
select calyear,monthno+1,dateadd(month,1, date1),dateadd(month,1, date2),dateadd(month,1, date3),dateadd(month,1, date4),dateadd(month,1, date5),dateadd(month,1, date6),dateadd(month,1, date7) from cte where calyear=2014 and monthno<=11
)
select * from cte
I have a query that pulls out month/year totals for customers, and add the ntile ranking. If I were to be able to pull out the max subtotal for ntile 1, 2, 3, 4, and 5, I would ALMOST get what I'm after, but I do not know how to proceed.
For example, the result I want would look something like:
Month Year CustomerCode SubTotal ntile
1 2012 CCC 131.45 1
1 2012 CCC 342.95 2
1 2012 ELITE 643.92 3
1 2012 CCC 1454.05 4
1 2012 CCC 12971.78 5
2 2012 CCC 135.99 1
2 2012 CCI 370.47 2
2 2012 NOC 766.84 3
2 2012 ELITE 1428.26 4
2 2012 VBC 5073.20 5
3 2012 CCC 119.02 1
3 2012 CCC 323.78 2
3 2012 HUCC 759.66 3
3 2012 ELITE 1402.95 4
3 2012 CCC 7964.20 5
EXCEPT - I would expect ranking to be different customers like for month 2, but my base query isn't giving me that result - and I obviously don't know how to get it in T-SQL on SQL SERVER 2005 - in fact I'm not sure what I'm getting.
My next option is to pull a DataTable in C# and do some gymnastics to get there, but there has to be an easier way :)
My base query is
SELECT
i.DateOrdered
,LTRIM(STR(DATEPART(MONTH,i.DateOrdered))) AS [Month]
,LTRIM(STR(YEAR(i.Dateordered))) AS [Year]
,c.CustomerCode
,SUM(i.Jobprice) AS Subtotal
,NTILE(5) OVER(ORDER BY SUM(i.JobPrice)) AS [ntile]
FROM Invoices i
JOIN
Customers c
ON i.CustomerID = c.ID
WHERE i.DateOrdered >= '1/1/2012'
AND i.DateOrdered <= '9/30/2012'
GROUP BY YEAR(i.DateOrdered), MONTH(i.DateOrdered), i.DateOrdered, c.CustomerCode
ORDER BY LTRIM(STR(DATEPART(MONTH,i.DateOrdered))),
TRIM(STR(YEAR(i.Dateordered))),
SUM(i.JobPrice), c.CustomerCode ASC
I'd really appreciate help getting this right.
Thanks in advance
Cliff
If I read you correctly, what you are after is
For each month in the range,
Show 5 customers who have the greatest SUMs in that month
And against each customer, show the corresponding SUM.
In that case, this SQL Fiddle creates a sample table and runs the query that gives you the output described above. If you wanted to see what's in the created tables, just do simple SELECTs on the right panel.
The query is:
; WITH G as -- grouped by month and customer
(
SELECT DATEADD(D,1-DAY(i.DateOrdered),i.DateOrdered) [Month],
c.CustomerCode,
SUM(i.Jobprice) Subtotal
FROM Invoices i
JOIN Customers c ON i.CustomerID = c.ID
WHERE i.DateOrdered >= '1/1/2012' AND i.DateOrdered <= '9/30/2012'
GROUP BY DATEADD(D,1-DAY(i.DateOrdered),i.DateOrdered), c.CustomerCode
)
SELECT MONTH([Month]) [Month],
YEAR([Month]) [Year],
CustomerCode,
SubTotal,
Rnk [Rank]
FROM
(
SELECT *, RANK() OVER (partition by [Month] order by Subtotal desc) Rnk
FROM G
) X
WHERE Rnk <= 5
ORDER BY Month, Rnk
To explain, the first part (WITH block) is just a fancy way of writing a subquery, that GROUPs the data by month and Customer. The expression DATEADD(D,1-DAY(i.DateOrdered),i.DateOrdered) turns every date into the FIRST day of that month, so that the data can be easily grouped by month. The next subquery written in traditional form adds a RANK column within each month by the subtotal, which is finally SELECTed to give the top 5*.
Note that RANK allows for equal rankings, which may end up showing 6 customers for a month, if 3 of them are ranked equally at position 4. If that is not what you want, then you can change the word RANK to ROW_NUMBER which will randomly tie-break between equal Subtotals.
The query needs to be modified to only get the month and year dateparts. The issue you are having with the same customer showing multiple times in the same month is due to the inclusion of i.DateOrdered in the select and group by clauses.
The following query should give you what you need. Also, I suspect it is a typo on the next to last line of the query, but tsql doesn't have a TRIM() function only LTRIM and RTRIM.
SELECT
LTRIM(STR(DATEPART(MONTH,i.DateOrdered))) AS [Month]
,LTRIM(STR(YEAR(i.Dateordered))) AS [Year]
,c.CustomerCode
,SUM(i.Jobprice) AS Subtotal
,NTILE(5) OVER(ORDER BY SUM(i.JobPrice)) AS [ntile]
FROM Invoices i
JOIN
Customers c
ON i.CustomerID = c.ID
WHERE i.DateOrdered >= '1/1/2012'
AND i.DateOrdered <= '9/30/2012'
GROUP BY YEAR(i.DateOrdered), MONTH(i.DateOrdered), c.CustomerCode
ORDER BY LTRIM(STR(DATEPART(MONTH,i.DateOrdered))),
LTRIM(STR(YEAR(i.Dateordered))),
SUM(i.JobPrice), c.CustomerCode ASC
This gives these results
Month Year CustomerCode Subtotal ntile
1 2012 ELITE 643.92 2
1 2012 CCC 14900.23 5
2 2012 CCC 135.99 1
2 2012 CCI 370.47 1
2 2012 NOC 766.84 3
2 2012 ELITE 1428.26 4
2 2012 VBC 5073.20 4
3 2012 HUCC 759.66 2
3 2012 ELITE 1402.95 3
3 2012 CCC 8407.00 5
Try this:
declare #tab table
(
[month] int,
[year] int,
CustomerCode varchar(20),
SubTotal float
)
insert into #tab
select
1,2012,'ccc',131.45 union all
select
1,2012,'ccc',343.45 union all
select
1,2012,'ELITE',643.92 union all
select
2,2012,'ccc',131.45 union all
select
2,2012,'ccc',343.45 union all
select
2,2012,'ELITE',643.92 union all
select
3,2012,'ccc',131.45 union all
select
3,2012,'ccc',343.45 union all
select
3,2012,'ELITE',643.92
;with cte as
(
select NTILE(3) OVER(partition by [month] ORDER BY [month]) AS [ntile],* from #tab
)
select * from cte
Even in your base query you need to add partition by, so that you will get correct output.
I can't see how to solve this problem without double ranking:
You need to get the largest sums per customer & month.
You then need, for every month, to retrieve the top five of the found sums.
Here's how I would approach this:
;
WITH MaxSubtotals AS (
SELECT DISTINCT
CustomerID,
MonthDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, DateOrdered), 0),
Subtotal = MAX(SUM(JobPrice)) OVER (
PARTITION BY Customer, DATEADD(MONTH, DATEDIFF(MONTH, 0, DateOrdered), 0)
ORDER BY SUM(JobPrice)
)
FROM Invoices
GROUP BY
CustomerID,
DateOrdered
),
TotalsRanked AS (
SELECT
CustomerID,
MonthDate,
Subtotal,
Ranking = ROW_NUMBER() OVER (PARTITION BY MonthDate ORDER BY Subtotal DESC)
FROM MaxDailyTotals
)
SELECT
Month = MONTH(i.MonthDate),
Year = YEAR(i.MonthDate),
c.CustomerCode,
i.Subtotal,
i.Ranking
FROM TotalsRanked i
INNER JOIN Customers ON i.CustomerID = c.ID
WHERE i.Ranking <= 5
;
The first CTE, MaxSubtotals, determines the maximum subtotals per customer & month. Involving DISTINCT and a window aggregating function, it is essentially a "shortcut" for the following two-step query:
SELECT
CustomerID,
MonthDate,
Subtotal = MAX(Subtotal)
FROM (
SELECT
CustomerID,
MonthDate = DATEADD(MONTH, DATEDIFF(MONTH, 0, DateOrdered), 0),
Subtotal = SUM(JobPrice)
FROM Invoices
GROUP BY
CustomerID,
DateOrdered
) s
GROUP BY
CustomerID,
MonthDate
The other CTE, TotalsRanked, simply adds ranking numbers for the found susbtotals, partitioning by customer and month. As a final step, you only need to limit the rows to those that have rankings not greater than 5 (or whatever you might choose another time).
Note that using ROW_NUMBER() to rank the rows in this case guarantees that you'll get no more than 5 rows with the Ranking <= 5 filter. If there were two or more rows with the same subtotal, the would get distinct rankings, and in the end you might end up with an output like this:
Month Year CustomerCode Subtotal Ranking
----- ---- ------------ -------- -------
1 2012 CCC 1500.00 1
1 2012 ELITE 1400.00 2
1 2012 NOC 900.00 3
1 2012 VBC 700.00 4
1 2012 HUCC 700.00 5
-- 1 2012 ABC 690.00 6 -- not returned
-- 1 2012 ... ... ...
Even though there might be other customers with Subtotals of 700.00 for the same month, they wouldn't be returned, because they would be assigned rankings after 5.
You could use RANK() instead of ROW_NUMBER() to account for that. But note that you might end up with more than 5 rows per month then, with an output like this:
Month Year CustomerCode Subtotal Ranking
----- ---- ------------ -------- -------
1 2012 CCC 1500.00 1
1 2012 ELITE 1400.00 2
1 2012 NOC 900.00 3
1 2012 VBC 700.00 4
1 2012 HUCC 700.00 4
1 2012 ABC 700.00 4
-- 1 2012 DEF 690.00 7 -- not returned
-- 1 2012 ... ... ...
Customers with subtotals less than 700.00 wouldn't make it to the output because they would have rankings starting with 7, which would correspond to the ranking of the first under-700.00 sum if ranked by ROW_NUMBER().
And there's another option, DENSE_RANK(). You might want to use it if you want up to 5 distinct sums per month in your output. With DENSE_RANK() your output might contain even more rows per month than it would have with RANK(), but the number of distinct subtotals would be exactly 5 (or fewer if the original dataset can't provide you with 5). That is, your output might then look like this:
Month Year CustomerCode Subtotal Ranking
----- ---- ------------ -------- -------
1 2012 CCC 1500.00 1
1 2012 ELITE 1400.00 2
1 2012 NOC 900.00 3
1 2012 VBC 700.00 4
1 2012 HUCC 700.00 4
1 2012 ABC 700.00 4
1 2012 DEF 650.00 5
1 2012 GHI 650.00 5
1 2012 JKL 650.00 5
-- 1 2012 MNO 600.00 5 -- not returned
-- 1 2012 ... ... ...
Like RANK(), the DENSE_RANK() function assigns same rankings to identical values, but, unlike RANK(), it doesn't produce gaps in the ranking sequence.
References:
OVER Clause (Transact-SQL)
Ranking Functions (Transact-SQL)
I do not quite understand why those two different codesamples return a different value.
somehow incorrect but working syntax, returns false results, e.g it returns 0 when the comparison is done over two equal values:
(SELECT CASE
WHEN
SUM(V.IsCompatible) OVER
(PARTITION BY ComputerName, UserID) = ApplicationCount
THEN 1 ELSE 0 END
) AS CompatibleUser
The one below returns the correct values, ie. 1 when there are two equal values compared.
(CASE
WHEN
SUM(V.IsCompatible) OVER
(PARTITION BY ComputerName, UserID) = ApplicationCount
THEN 1 ELSE 0 END
) AS CompatibleUser
or even simpler:
(SELECT CASE
WHEN
X = Y
THEN 1 ELSE 0 END
) AS Result
X = 22 AND Y = 22 => Result = 0
(CASE
WHEN
X = Y
THEN 1 ELSE 0 END
) AS Result
X = 22 AND Y = 22 => Result = 1
I understand applying the correct syntax is important, and I am aware of the SELECT CASE syntax in T-SQL, but I do not understand how the first code sample is evaluated and delivering an unexpected result.
update: full query in it's context
select userapplication.username,
computerdetails.computername,
sum(userapplication.iscompatible)
over (partition by computerdetails.computername,
userapplication.userid) as compatiblecount,
userapplication.applicationcount,
( case
when sum(userapplication.iscompatible)
over (partition by
computerdetails.computername,
userapplication.userid) <> userapplication.applicationcount
then 0
else 1
end
) as usercomputeriscompatible
from computerdetails
right outer join usercomputer
on computerdetails.computerid = usercomputer.computerid
right outer join userapplication
on usercomputer.gebruikerid = userapplication.userid
so userComputerIsCompatible is the result in question here
I think the reason for this behavior is the next one: the expressions like (SELECT ...) are considered to be sub-queries even they don't have FROM clause. Is assume the source of data for these (false) "sub-queries" is only the current row. So, (SELECT expression) is interpreted as (SELECT expression FROM current_row) and (SELECT SUM(iscompatible)OVER(...)) is executed as (SELECT SUM(iscompatible)OVER(current_row)).
Argument: analyzing execution plan for (SELECT SUM(IsWeb) OVER(PARTITION BY OrderDate) [FROM current_row]) expression
I see a Constant Scan (Scan an internal table of constants) operator instead of Clustered Index Scan before Segment and Stream Aggregate ([Expr1007] = Scalar Operator(SUM(#OrderHeader.[IsWeb] as [h].[IsWeb]))) operators. This internal table (Constant Scan) is constructed from current row.
Example (tested with SQL2005SP3 and SQL2008):
DECLARE #OrderHeader TABLE
(
OrderHeaderID INT IDENTITY PRIMARY KEY
,OrderDate DATETIME NOT NULL
,IsWeb TINYINT NOT NULL --or BIT
);
INSERT #OrderHeader
SELECT '20110101', 0
UNION ALL
SELECT '20110101', 1
UNION ALL
SELECT '20110101', 1
UNION ALL
SELECT '20110102', 1
UNION ALL
SELECT '20110103', 0
UNION ALL
SELECT '20110103', 0;
SELECT *
,SUM(IsWeb) OVER(PARTITION BY OrderDate) SumExpression_1
FROM #OrderHeader h
ORDER BY h.OrderDate;
SELECT *
,(SELECT SUM(IsWeb) OVER(PARTITION BY OrderDate)) SumWithSubquery_2
FROM #OrderHeader h
ORDER BY h.OrderDate;
Results:
OrderHeaderID OrderDate IsWeb SumExpression_1
------------- ----------------------- ----- ---------------
1 2011-01-01 00:00:00.000 0 2
2 2011-01-01 00:00:00.000 1 2
3 2011-01-01 00:00:00.000 1 2
4 2011-01-02 00:00:00.000 1 1
5 2011-01-03 00:00:00.000 0 0
6 2011-01-03 00:00:00.000 0 0
OrderHeaderID OrderDate IsWeb SumWithSubquery_2
------------- ----------------------- ----- -----------------
1 2011-01-01 00:00:00.000 0 0
2 2011-01-01 00:00:00.000 1 1
3 2011-01-01 00:00:00.000 1 1
4 2011-01-02 00:00:00.000 1 1
5 2011-01-03 00:00:00.000 0 0
6 2011-01-03 00:00:00.000 0 0
I tried your code and I get the same results for both queries. Here's the code I tried:
DECLARE #X INT = 22
DECLARE #Y INT = 22
SELECT (SELECT CASE
WHEN
#X = #Y
THEN 1 ELSE 0 END
) AS Result
SELECT (CASE
WHEN
#X = #Y
THEN 1 ELSE 0 END
) AS Result
Result is 1 and 1
I ran this on SQL Server 2008 R2
I've got a set of data that looks something like this (VERY simplified):
productId Qty dateOrdered
--------- --- -----------
1 2 10/10/2008
1 1 11/10/2008
1 2 10/10/2009
2 3 10/12/2009
1 1 10/15/2009
2 2 11/15/2009
Out of this, we're trying to create a query to get something like:
productId Year Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
--------- ---- --- --- --- --- --- --- --- --- --- --- --- ---
1 2008 0 0 0 0 0 0 0 0 0 2 1 0
1 2009 0 0 0 0 0 0 0 0 0 3 0 0
2 2009 0 0 0 0 0 0 0 0 0 3 2 0
The way I'm doing this now, I'm doing 12 selects, one for each month, and putting those in temp tables. I then do a giant join. Everything works, but this guy is dog slow.
select productId, Year(dateOrdered) Year
,isnull(sum(case when month(dateOrdered) = 1 then Qty end), 0) Jan
,isnull(sum(case when month(dateOrdered) = 2 then Qty end), 0) Feb
,isnull(sum(case when month(dateOrdered) = 3 then Qty end), 0) Mar
,isnull(sum(case when month(dateOrdered) = 4 then Qty end), 0) Apr
,isnull(sum(case when month(dateOrdered) = 5 then Qty end), 0) May
,isnull(sum(case when month(dateOrdered) = 6 then Qty end), 0) Jun
,isnull(sum(case when month(dateOrdered) = 7 then Qty end), 0) Jul
,isnull(sum(case when month(dateOrdered) = 8 then Qty end), 0) Aug
,isnull(sum(case when month(dateOrdered) = 9 then Qty end), 0) Sep
,isnull(sum(case when month(dateOrdered) = 10 then Qty end), 0) Oct
,isnull(sum(case when month(dateOrdered) = 11 then Qty end), 0) Nov
,isnull(sum(case when month(dateOrdered) = 12 then Qty end), 0) Dec
from Table1
group by productId, Year(dateOrdered)
SQL Fiddle
SELECT productId, YEAR,
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=1),0) as 'JAN',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=2),0) as 'FEB',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=3),0) as 'MAR',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=4),0) as 'APR',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=5),0) as 'MAY',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=6),0) as 'JUN',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=7),0) as 'JUL',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=8),0) as 'AUG',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=9),0) as 'SEP',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=10),0) as 'OCT',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=11),0) as 'NOV',
ISNULL((SELECT SUM(Qty) FROM Product WHERE productId=X.productId AND YEAR=YEAR(dateOrdered) AND MONTH(dateOrdered)=12),0) as 'DEC'
FROM (
SELECT productId, YEAR(dateOrdered) AS YEAR FROM Product
GROUP BY YEAR(dateOrdered),ProductId) X
For those using Big Query, you can use the following:
select *
from UNNEST(GENERATE_DATE_ARRAY('2015-10-01', '2019-10-01', INTERVAL 1 MONTH))
See https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators?hl=fr#generate_date_array
You can use either a Union of your queries rather than temp tables or use the pivot option.
Here's a forum discussion on it:
Sql Server Forums - Show the row-wise data as column-wise
This qualifies as a presentation concern.
Presentation and SQL don't always mix well.
Isolating your presentation logic in the application layer will:
save you maintenance time—change your application code, but keep your SQL intact;
enable you to more quickly adapt to ephemeral client requirements;
give you more satisfaction than fiddling with a cross-tab or pivot-table that maddeningly does almost exactly what you want.
Below is an example of how you might do this in Python (you can use the excellent pyodbc module to connect to SQL Server):
from collections import defaultdict
from datetime import date
dd = defaultdict(int)
# input
rows = [(1,2,date(2008,10,10)), (1,1,date(2008,11,10)),
(1,2,date(2009,10,10)), (2,3,date(2009,10,12)),
(1,1,date(2009,10,15)), (2,2,date(2009,11,15))]
for row in rows:
# row[0] == productId
# row[1] == Qty
# row[2] == dateOrdered
# pyodbc enables referring to column names by name
dd[(row[2].year, row[2].month, row[0])] += row[1]
presentation_rows = sorted(set((i[0], i[2]) for i in dd.keys()))
for i in presentation_rows:
print i[1], i[0],
for j in range(0,13):
try:
print dd[i[0], j, i[1]],
except IndexError:
print 0,
print
# output
# 1 2008 0 0 0 0 0 0 0 0 0 0 2 1 0
# 1 2009 0 0 0 0 0 0 0 0 0 0 3 0 0
# 2 2009 0 0 0 0 0 0 0 0 0 0 3 2 0
try this. So this code will select data within certain time range, then convert it to a new column. For example, in my sql code: it selects time range between '2014-10-01' and '2014-10-31' from column 'L_dt', then create a new column called "October". In this way, we can lay out data at different columns originated from one column.
select
sum(case when L_dt between '2014-10-01' and '2014-10-31' then 1 else 0 end) October,
sum(case when L_dt between '2014-11-01' and '2014-11-30' then 1 else 0 end) November,
sum(case when L_dt between '2014-12-01' and '2014-12-31' then 1 else 0 end) December
from Table;
If the input looks like:
L_dt
2014-10-13
2014-12-21
2014-11-22
2014-10-10
Then the output will be
+---------+----------+----------+
| October | November | December |
+---------+----------+----------+
| 2 | 1 | 1 |
+---------+----------+----------+