SQL Server tabulating survey data for every month - sql-server

I have a survey table with many columns but i am focusing on these 2, survey_date and over_rating. i am not sure if it is possible to be done in a single query. I am using sql server 2012. This is my sample data.
select survey_date, overall_rating from survey
survey_date overall_rating
2017-01-06 15:09:51.940 6
2017-02-06 14:18:18.620 4
2017-05-07 16:03:12.037 7
2017-05-23 10:41:30.357 7
2017-05-23 10:41:30.357 5
2017-05-24 12:05:25.217 8
2017-06-01 09:03:47.727 7
2017-06-05 09:01:07.283 9
2017-06-05 09:28:12.597 6
2017-06-15 09:47:29.407 7
2017-07-06 12:10:50.003 2
2017-07-06 13:45:52.997 7
2017-08-06 14:00:35.403 5
2017-08-09 12:21:17.367 8
I need to count the occurrence for each rating 1-10, for each month, and sum it up. Example June 15, rating 10 have 1, rating 9 have 10, ...
This is the result table:
Month 10 9 8 7 6 5 4 3 2 1 Avg Score Total Total >=6 CSI
June'15 1 10 20 3 0 0 0 0 0 0 8 34 34 100%
July'15 1 16 14 0 0 0 0 0 1 0 9 32 31 99%
August'15 7 6 6 0 0 0 0 0 0 0 9 19 19 100%
September'15 0 2 2 0 0 0 0 0 0 0 9 4 4 100%
November'15 0 1 2 0 0 0 0 0 0 0 8 3 3 100%
December'15 0 7 3 4 2 0 0 0 0 0 8 16 16 100%
i have tried this query but is partly wrong as there is duplicate month for each rating:
select si.yr, si.mn,
case when si.overall_rating = 10 then count(si.overall_rating) else 0 end as '10',
case when si.overall_rating = 9 then count(si.overall_rating) else 0 end as '9',
case when si.overall_rating = 8 then count(si.overall_rating) else 0 end as '8',
case when si.overall_rating = 7 then count(si.overall_rating) else 0 end as '7',
case when si.overall_rating = 6 then count(si.overall_rating) else 0 end as '6',
case when si.overall_rating = 5 then count(si.overall_rating) else 0 end as '5',
case when si.overall_rating = 4 then count(si.overall_rating) else 0 end as '4',
case when si.overall_rating = 3 then count(si.overall_rating) else 0 end as '3',
case when si.overall_rating = 2 then count(si.overall_rating) else 0 end as '2',
case when si.overall_rating = 1 then count(si.overall_rating) else 0 end as '1',
sum(si.overall_rating) as month_count
from
(select YEAR(s.survey_date) yr, MONTH(s.survey_date) mn, s.overall_rating
from survey s where s.status='Submitted' and s.survey_date >= '2017-01-01' AND s.survey_date <= '2017-12-31'
group by YEAR(s.survey_date), MONTH(s.survey_date), s.overall_rating) si group by si.yr, si.mn, si.overall_rating;
Results:
yr mm 10 9 8 7 6 5 4 3 2 1 total
2017 1 0 0 0 0 1 0 0 0 0 0 6
2017 2 0 0 0 0 0 0 1 0 0 0 4
2017 5 0 0 0 0 0 1 0 0 0 0 5
2017 5 0 0 0 1 0 0 0 0 0 0 7
2017 5 0 0 1 0 0 0 0 0 0 0 8
2017 6 0 0 0 0 1 0 0 0 0 0 6
2017 6 0 0 0 1 0 0 0 0 0 0 7
2017 6 0 1 0 0 0 0 0 0 0 0 9
2017 7 0 0 0 0 0 0 0 0 1 0 2
2017 7 0 0 0 1 0 0 0 0 0 0 7
2017 8 0 0 0 0 0 1 0 0 0 0 5
2017 8 0 0 1 0 0 0 0 0 0 0 8
As you can see 5 and 6 are repeated for different rating. If anyone could tell me is it possible to be done in a single query. Thanks

I think I understand what you are trying to achieve here and you'll be pleased to know you are not far off. You have the right idea in using a conditional aggregate, but you need to wrap your conditional case expression in the aggregate, not the other way around. To do a conditional count, you can simply return 1 for a condition match and 0 for a no match and then sum up the result.
Doing this allows your group by to remain nice and simple:
declare #t table(survey_date datetime,overall_rating int);
insert into #t values ('2017-01-06 15:09:51.940',6),('2017-02-06 14:18:18.620',4),('2017-05-07 16:03:12.037',7),('2017-05-23 10:41:30.357',7),('2017-05-23 10:41:30.357',5),('2017-05-24 12:05:25.217',8),('2017-06-01 09:03:47.727',7),('2017-06-05 09:01:07.283',9),('2017-06-05 09:28:12.597',6),('2017-06-15 09:47:29.407',7),('2017-07-06 12:10:50.003',2),('2017-07-06 13:45:52.997',7),('2017-08-06 14:00:35.403',5),('2017-08-09 12:21:17.367',8);
select dateadd(m,datediff(m,0,survey_date),0) as [Month]
,sum(case when overall_rating = 10 then 1 else 0 end) as [10]
,sum(case when overall_rating = 9 then 1 else 0 end) as [9]
,sum(case when overall_rating = 8 then 1 else 0 end) as [8]
,sum(case when overall_rating = 7 then 1 else 0 end) as [7]
,sum(case when overall_rating = 6 then 1 else 0 end) as [6]
,sum(case when overall_rating = 5 then 1 else 0 end) as [5]
,sum(case when overall_rating = 4 then 1 else 0 end) as [4]
,sum(case when overall_rating = 3 then 1 else 0 end) as [3]
,sum(case when overall_rating = 2 then 1 else 0 end) as [2]
,sum(case when overall_rating = 1 then 1 else 0 end) as [1]
,count(overall_rating) as ScoresReturned
,sum(overall_rating) as TotalScore
,avg(cast(overall_rating as decimal(10,0))) as Average
from #t
group by dateadd(m,datediff(m,0,survey_date),0)
order by [Month];
Output:
+-------------------------+----+---+---+---+---+---+---+---+---+---+----------------+------------+----------+
| Month | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | ScoresReturned | TotalScore | Average |
+-------------------------+----+---+---+---+---+---+---+---+---+---+----------------+------------+----------+
| 2017-01-01 00:00:00.000 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 6 | 6.000000 |
| 2017-02-01 00:00:00.000 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 4 | 4.000000 |
| 2017-05-01 00:00:00.000 | 0 | 0 | 1 | 2 | 0 | 1 | 0 | 0 | 0 | 0 | 4 | 27 | 6.750000 |
| 2017-06-01 00:00:00.000 | 0 | 1 | 0 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 4 | 29 | 7.250000 |
| 2017-07-01 00:00:00.000 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 2 | 9 | 4.500000 |
| 2017-08-01 00:00:00.000 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 2 | 13 | 6.500000 |
+-------------------------+----+---+---+---+---+---+---+---+---+---+----------------+------------+----------+

select si.yr, si.mn,
case when si.overall_rating = 10 then count(si.overall_rating) else 0 end as '10',
case when si.overall_rating = 9 then count(si.overall_rating) else 0 end as '9',
case when si.overall_rating = 8 then count(si.overall_rating) else 0 end as '8',
case when si.overall_rating = 7 then count(si.overall_rating) else 0 end as '7',
case when si.overall_rating = 6 then count(si.overall_rating) else 0 end as '6',
case when si.overall_rating = 5 then count(si.overall_rating) else 0 end as '5',
case when si.overall_rating = 4 then count(si.overall_rating) else 0 end as '4',
case when si.overall_rating = 3 then count(si.overall_rating) else 0 end as '3',
case when si.overall_rating = 2 then count(si.overall_rating) else 0 end as '2',
case when si.overall_rating = 1 then count(si.overall_rating) else 0 end as '1',
sum(si.overall_rating) as month_count
from
(select YEAR(s.survey_date) yr, MONTH(s.survey_date) mn, s.overall_rating
from survey s where s.status='Submitted' and s.survey_date >= '2017-01-01' AND s.survey_date <= '2017-12-31'
group by YEAR(s.survey_date), MONTH(s.survey_date), s.overall_rating) si group by si.yr, si.mn;
You must remove si.overall_rating in Group By

Oh! I posted late, anyway you can try this otherwise.
DATA:
IF ( OBJECT_ID('tempdb..#temptable') IS NOT NULL )
BEGIN
DROP TABLE #temptable
END
CREATE TABLE #temptable
(
survey_date DATETIME ,
overall_rating NUMERIC(22,2)
)
INSERT INTO #temptable
( survey_date, overall_rating )
VALUES ( '2017-01-06 15:09:51.940', 6 ),
( '2017-02-06 14:18:18.620', 4 ),
( '2017-05-07 16:03:12.037', 7 ),
( '2017-05-23 10:41:30.357', 7 ),
( '2017-05-23 10:41:30.357', 5 ),
( '2017-05-24 12:05:25.217', 8 ),
( '2017-06-01 09:03:47.727', 7 ),
( '2017-06-05 09:01:07.283', 9 ),
( '2017-06-05 09:28:12.597', 6 ),
( '2017-06-15 09:47:29.407', 7 ),
( '2017-07-06 12:10:50.003', 2 ),
( '2017-07-06 13:45:52.997', 7 ),
( '2017-08-06 14:00:35.403', 5 ),
( '2017-08-09 12:21:17.367', 8 )
QUERY:
;
WITH CTE
AS ( SELECT DATENAME(month, survey_date) + ' '''
+ RIGHT(CAST(YEAR(survey_date) AS NVARCHAR(4)),
2) AS [Month] ,
ISNULL([1], 0) [1] ,
ISNULL([2], 0) [2] ,
ISNULL([3], 0) [3] ,
ISNULL([4], 0) [4] ,
ISNULL([5], 0) [5] ,
ISNULL([6], 0) [6] ,
ISNULL([7], 0) [7] ,
ISNULL([8], 0) [8] ,
ISNULL([9], 0) [9] ,
ISNULL([10], 0) [10],
Total,
Average
FROM ( SELECT survey_date ,
COUNT(overall_rating) overall_rating,
CAST(SUM(overall_rating) AS INT) Total,
AVG(overall_rating) Average
FROM ( SELECT DATEADD(MONTH,
DATEDIFF(MONTH,
0, survey_date),
0) survey_date ,
overall_rating
FROM #temptable
) T
GROUP BY t.survey_date
) PVT PIVOT ( SUM(overall_rating) FOR overall_rating IN ( [1],
[2], [3], [4],
[5], [6], [7],
[8], [9], [10] ) ) P
)
SELECT [Month] ,
ISNULL([1], 0) [1] ,
ISNULL([2], 0) [2] ,
ISNULL([3], 0) [3] ,
ISNULL([4], 0) [4] ,
ISNULL([5], 0) [5] ,
ISNULL([6], 0) [6] ,
ISNULL([7], 0) [7] ,
ISNULL([8], 0) [8] ,
ISNULL([9], 0) [9] ,
ISNULL([10], 0) [10],
Total,
Average
FROM CTE
RESULT:
Month 1 2 3 4 5 6 7 8 9 10 Total Average
----------------------- ------ ---- ----- ----- ----- ----- ---- ---- ----- ----- ----------- -------------
January '17 1 0 0 0 0 0 0 0 0 0 6 6.000000
February '17 1 0 0 0 0 0 0 0 0 0 4 4.000000
May '17 0 0 0 4 0 0 0 0 0 0 27 6.750000
June '17 0 0 0 4 0 0 0 0 0 0 29 7.250000
July '17 0 2 0 0 0 0 0 0 0 0 9 4.500000
August '17 0 2 0 0 0 0 0 0 0 0 13 6.500000
(6 row(s) affected)

Related

MSSQL - Counting dates from two columns grouping by external date range

I have a simple table containing case number (ID), opening_date and end_date where end_date have null values (unfinished cases). It looks like this:
ID opening_date end_date
1 2021-01-04 2021-01-14
2 2021-01-04 2021-01-26
3 2021-01-14 2021-02-15
4 2021-02-01 NULL
5 2021-02-04 2021-02-26
6 2021-02-10 2021-02-15
I'm trying to write a select query which will show me simply by month/week or day (nevermind), how many cases were set up (opening_date) and how many were closed (end_date) per each month./week. The problem is that I cannot use opening or end date in filters because not every date from opening_date column is in end_date and vice versa. It should be specific date range generated separately as external table in first column or something like that, so if there's situation, where neither opening nor end date occurs (in a day/week/month), a row with zeros should be shown as in the first date below - The result of example by day:
date openings endings
2021-01-01 0 0
2021-01-02 0 0
2021-01-03 0 0
2021-01-04 2 0
2021-01-05 0 0
2021-01-06 0 0
2021-01-07 0 0
2021-01-08 0 0
2021-01-09 0 0
2021-01-10 0 0
2021-01-11 0 0
2021-01-12 0 0
2021-01-13 0 0
2021-01-14 1 1
2021-01-15 0 0
2021-01-16 0 0
2021-01-17 0 0
2021-01-18 0 0
2021-01-19 0 0
2021-01-20 0 0
2021-01-21 0 0
2021-01-22 0 0
2021-01-23 0 0
2021-01-24 0 0
2021-01-25 0 0
2021-01-26 0 1
2021-01-27 0 0
2021-01-28 0 0
2021-01-29 0 0
2021-01-30 0 0
2021-01-31 0 0
2021-02-01 1 0
2021-02-02 0 0
2021-02-03 0 0
2021-02-04 1 0
2021-02-05 0 0
2021-02-06 0 0
2021-02-07 0 0
2021-02-08 0 0
2021-02-09 0 0
2021-02-10 1 0
2021-02-11 0 0
2021-02-12 0 0
2021-02-13 0 0
2021-02-14 0 0
2021-02-15 0 2
2021-02-16 0 0
2021-02-17 0 0
2021-02-18 0 0
2021-02-19 0 0
2021-02-20 0 0
2021-02-21 0 0
2021-02-22 0 0
2021-02-23 0 0
2021-02-24 0 0
2021-02-25 0 0
2021-02-26 0 1
2021-02-27 0 0
2021-02-28 0 0
By months:
Month openings endings
2021-01 3 2
2021-02 3 3
Please help me. Thanks in advance.
You need a calendar table for this. You start with the calendar, and LEFT JOIN everything else.
To get the calculation for each day, we can unpivot and group, then count daily totals
You can have a real table. Or you can generate it on the fly, like this:
WITH
L0 AS ( SELECT c = 1
FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1),
(1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ),
L1 AS ( SELECT c = 1 FROM L0 A, L0 B, L0 C ),
Nums AS ( SELECT rownum = ROW_NUMBER() OVER(ORDER BY (SELECT 1))
FROM L1 ),
Dates AS ( SELECT [date] = DATEADD(day, rownum, '20180101')
FROM Nums )
SELECT
d.[date],
openings = ISNULL(t.openings, 0),
endings = ISNULL(t.endings, 0)
FROM Dates d
LEFT JOIN (
SELECT v.AllDates,
openings = COUNT(IsOpen),
endings = COUNT(IsEnd)
FROM YourTable t
CROSS APPLY (VALUES
(opening_date, 1, NULL),
(end_date, NULL, 1)
) v(AllDates, IsOpen, IsEnd)
GROUP BY v.AllDates
) t ON t.AllDates = d.[date];

SQL Pivot Table - Subqueries

I have a program that is logging the time a user spends on certain aspects, some are identified as specific "time". I'm struggling to get multiple lines of Grouped query results into a single line for each month as a "summary".
My current query:
SELECT
TotalMins = SUM(Minutes)
,DateMonth = MONTH(Date)
,ID1
,PC
FROM User_Time_Log
WHERE
(UserID = 1)
AND (YEAR(Date) = 2018)
GROUP BY
MONTH(Date)
,ID1
,PC1
Current results:
TotalMins DateMonth ID1 PC1
192 1 0 0
306 1 0 100
113 2 0 0
365 2 0 100
14 2 1 0
3 2 1 100
75 3 0 0
253 3 0 100
3 3 1 0
300 4 0 0
233 4 0 100
10 4 1 0
23 4 1 100
438 5 0 0
134 5 0 100
19 5 1 0
49 5 1 100
0 9 1 0
11 10 0 0
21 10 0 60
167 10 1 100
What I would like to do from this point is to create a table showing all 12 months, regardless of whether there is information within that month or not, and show the relative information within each row for that month. for example:
DateMonth NonID1 TimeID1 TimePC1 (Round((PC1/100)*TotalMins)) TimePC1ID1
1 192 0 306 0
2 113 14 365 3
3 75 3 253 0
4 300 10 233 23
5 438 19 134 49
6 0 0 0 0
7 0 0 0 0
8 0 0 0 0
9 0 0 0 0
10 11 0 13 167
11 0 0 0 0
12 0 0 0 0
What's the most efficient way to do this?
Note: I have also created a table to give me 1-12 as rows that I can use to give me the months that I need to use, where information is not within the user_time_log.
Here's a simple way to do what you're looking for:
First, create your table of month values. I made a simple temp table with a single column.
CREATE TABLE #Dates (MonthNum INT)
INSERT INTO #Dates
(
MonthNum
)
VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12)
Next, you can put your existing query into a CTE, then LEFT JOIN to your table of months. You'll want to put your columns into a SUM'd CASE statement, like so:
;WITH Aggregation AS
(
SELECT
TotalMins = SUM(Minutes)
,DateMonth = MONTH(Date)
,ID1
,PC1
FROM #User_Time_Log
WHERE
(UserID = 1)
AND (YEAR(Date) = 2018)
GROUP BY
MONTH(Date)
,ID1
,PC1
)
SELECT
d.MonthNum
,NonID1 = SUM(CASE WHEN ID1 = 0 THEN TotalMins ELSE 0 END)
,TimeID1 = SUM(CASE WHEN ID1 = 1 THEN TotalMins ELSE 0 END)
,TimePC1 = SUM(CASE WHEN ID1 = 0 THEN ROUND((PC1/100)*TotalMins,0) ELSE 0 END)
,TimePC1ID1 = SUM(CASE WHEN ID1 = 1 THEN ROUND((PC1/100)*TotalMins,0) ELSE 0 END)
FROM #Dates d
LEFT JOIN Aggregation a ON d.MonthNum = a.DateMonth
GROUP BY d.MonthNum
Output would then look like this:
MonthNum NonID1 TimeID1 TimePC1 TimePC1ID1
1 498 0 306 0
2 478 17 365 3
3 328 3 253 0
4 533 33 233 23
5 572 68 134 49
6 0 0 0 0
7 0 0 0 0
8 0 0 0 0
9 0 0 0 0
10 32 167 0 167
11 0 0 0 0
12 0 0 0 0
EDIT:
The ROUND() function call can be changed slightly to accomodate your need for decimal results. The first parameter of ROUND() is the expression you want to round, and the second is the number of decimal places to round to. Positive numbers indicate the number of places to the right of the decimal to round to. Negative numbers indicate the number of places to the left of the decimal to round to. So if you set it to 2, you'll get an answer rounded to the nearest hundredth.
But there's one more tweak we need. PC1 and TotalMins are both assumed to be INTs in my answer. So we have to give the SQL engine a little help so that it calculates the answer as a DECIMAL. By CAST()ing the INTs to DECIMALs, SQL will perform the arithmetic op as decimal math instead of integer math. You'd just have to change TimePC1 and TimePC1ID1 like so:
,TimePC1 = SUM(CASE WHEN ID1 = 0 THEN ROUND((CAST(PC1 AS DECIMAL)/100)*CAST(TotalMins AS DECIMAL),2) ELSE 0 END)
,TimePC1ID1 = SUM(CASE WHEN ID1 = 1 THEN ROUND((CAST(PC1 AS DECIMAL)/100)*CAST(TotalMins AS DECIMAL),2) ELSE 0 END)
Then the output looks like this:
MonthNum NonID1 TimeID1 TimePC1 TimePC1ID1
1 498 0 306.000000 0.000000
2 478 17 365.000000 3.000000
3 328 3 253.000000 0.000000
4 533 33 233.000000 23.000000
5 572 68 134.000000 49.000000
6 0 0 0.000000 0.000000
7 0 0 0.000000 0.000000
8 0 0 0.000000 0.000000
9 0 0 0.000000 0.000000
10 32 167 12.600000 167.000000
11 0 0 0.000000 0.000000
12 0 0 0.000000 0.000000

SQL query to get Groups and sub groups hierarchy

Account table
ac_id ac_name st_id
----------- ------------- -----------
1 LIABILITES 1
2 ASSET 1
3 REVENUE 1
4 EXPENSES 1
5 EQUITY 1
Groups table
grp_id grp_name ac_no grp_of st_id type_ cmp_id
----------- ------------------- ---------- -------- --------- --------- --------
1 Capital Account 1 0 1 0 0
2 Current Liability 1 0 1 0 0
3 Loan Liability 1 0 1 0 0
4 Suspense A/C 1 0 1 0 0
5 Current Assets 2 0 1 0 0
6 Fixed Assests 2 0 1 0 0
7 Investment 2 0 1 0 0
8 Misc. Expenses 2 0 1 0 0
9 Direct Income 3 0 1 0 0
10 Indirect Income 3 0 1 0 0
11 Sale Account 3 0 1 0 0
12 Direct Expense 4 0 1 0 0
13 Indirect Expense 4 0 1 0 0
14 Purchase Account 4 0 1 0 0
15 Sundry Creditors 2 1 1 0 0
16 Sundry Debitors 5 1 1 0 0
17 Bank Account 5 1 1 0 0
18 Cash In Hand 5 1 1 0 0
19 Duties & Taxes 2 1 1 0 0
20 Salary 12 1 1 0 0
21 Personal 5 1 1 0 0
22 Loan 2 0 1 0 0
23 Customer 16 1 1 0 0
34 Vendor 15 1 1 0 0
38 Sale Softwares 11 1 1 1 1
46 Stock In Hand 5 1 1 1 1
47 test 1 1 1 1 1
48 test in 47 1 1 1 1
Query to get all groups hierarchy.
declare #ac_no as int =2
;With CTE(grp_id,grp_name,ac_no,Level)
AS
( SELECT
grp_id,grp_name,ac_no,CAST(1 AS int)
FROM
Groups
WHERE
grp_id in (select grp_id from Groups where (ac_no=#ac_no) and grp_of=0)
UNION ALL
SELECT
o.grp_id,o.grp_name,o.ac_no,c.Level+1
FROM
Groups o
INNER JOIN
CTE c
ON c.grp_id=o.ac_no --where o.ac_no=2 and o.grp_of=1
)
select * from CTE
Result is ok for ac_no=2/3/4
grp_id grp_name ac_no Level
----------- ------------------- ----------- ------
5 Current Assets 2 1
6 Fixed Assests 2 1
7 Investment 2 1
8 Misc. Expenses 2 1
22 Loan 2 1
16 Sundry Debitors 5 2
17 Bank Account 5 2
18 Cash In Hand 5 2
21 Personal 5 2
46 Stock In Hand 5 2
23 Customer 16 3
But when I try to get result for ac_no=1;
I get error :
Msg 530, Level 16, State 1, Line 4
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
I think the issue is that you end up in an infinite recursion as you have a row that is it's own parent/child (eg. grp_id = ac_no).
I think it should work if you add a limiting clause to the recursive member like this:
DECLARE #ac_no AS int = 1;
WITH CTE (grp_id , grp_name , ac_no , Level ) AS (
SELECT grp_id, grp_name, ac_no, CAST( 1 AS int )
FROM Groups
WHERE grp_id IN (SELECT grp_id FROM Groups WHERE ac_no = #ac_no AND grp_of = 0)
UNION ALL
SELECT o.grp_id, o.grp_name, o.ac_no, c.Level + 1
FROM Groups o
INNER JOIN CTE c ON c.grp_id = o.ac_no --where o.ac_no=2 and o.grp_of=1
WHERE c.ac_no <> c.grp_id
)
SELECT * FROM CTE;

Where to find the size of SQL Server data types

I am trying to calculate the size of my database. I will have a table with 3 columns (id, int, money) I will have 26 million rows with all columns being occupied. How big will my database be? Also, where can I find the size of all SQL Server data types?
Your can use below query :
SELECT * FROM sys.types
result of above query is below :
name system_type_id user_type_id schema_id principal_id max_length precision scale collation_name is_nullable is_user_defined is_assembly_type default_object_id rule_object_id is_table_type
-------------------- -------------- ------------ --------- ------------ ---------- --------- ----- ----------------- ----------- --------------- ---------------- ----------------- -------------- -------------
image 34 34 4 NULL 16 0 0 NULL 1 0 0 0 0 0
text 35 35 4 NULL 16 0 0 Persian_100_CI_AI 1 0 0 0 0 0
uniqueidentifier 36 36 4 NULL 16 0 0 NULL 1 0 0 0 0 0
date 40 40 4 NULL 3 10 0 NULL 1 0 0 0 0 0
time 41 41 4 NULL 5 16 7 NULL 1 0 0 0 0 0
datetime2 42 42 4 NULL 8 27 7 NULL 1 0 0 0 0 0
datetimeoffset 43 43 4 NULL 10 34 7 NULL 1 0 0 0 0 0
tinyint 48 48 4 NULL 1 3 0 NULL 1 0 0 0 0 0
smallint 52 52 4 NULL 2 5 0 NULL 1 0 0 0 0 0
int 56 56 4 NULL 4 10 0 NULL 1 0 0 0 0 0
smalldatetime 58 58 4 NULL 4 16 0 NULL 1 0 0 0 0 0
real 59 59 4 NULL 4 24 0 NULL 1 0 0 0 0 0
money 60 60 4 NULL 8 19 4 NULL 1 0 0 0 0 0
datetime 61 61 4 NULL 8 23 3 NULL 1 0 0 0 0 0
float 62 62 4 NULL 8 53 0 NULL 1 0 0 0 0 0
sql_variant 98 98 4 NULL 8016 0 0 NULL 1 0 0 0 0 0
ntext 99 99 4 NULL 16 0 0 Persian_100_CI_AI 1 0 0 0 0 0
bit 104 104 4 NULL 1 1 0 NULL 1 0 0 0 0 0
decimal 106 106 4 NULL 17 38 38 NULL 1 0 0 0 0 0
numeric 108 108 4 NULL 17 38 38 NULL 1 0 0 0 0 0
smallmoney 122 122 4 NULL 4 10 4 NULL 1 0 0 0 0 0
bigint 127 127 4 NULL 8 19 0 NULL 1 0 0 0 0 0
hierarchyid 240 128 4 NULL 892 0 0 NULL 1 0 1 0 0 0
geometry 240 129 4 NULL -1 0 0 NULL 1 0 1 0 0 0
geography 240 130 4 NULL -1 0 0 NULL 1 0 1 0 0 0
varbinary 165 165 4 NULL 8000 0 0 NULL 1 0 0 0 0 0
varchar 167 167 4 NULL 8000 0 0 Persian_100_CI_AI 1 0 0 0 0 0
binary 173 173 4 NULL 8000 0 0 NULL 1 0 0 0 0 0
char 175 175 4 NULL 8000 0 0 Persian_100_CI_AI 1 0 0 0 0 0
timestamp 189 189 4 NULL 8 0 0 NULL 0 0 0 0 0 0
nvarchar 231 231 4 NULL 8000 0 0 Persian_100_CI_AI 1 0 0 0 0 0
nchar 239 239 4 NULL 8000 0 0 Persian_100_CI_AI 1 0 0 0 0 0
xml 241 241 4 NULL -1 0 0 NULL 1 0 0 0 0 0
sysname 231 256 4 NULL 256 0 0 Persian_100_CI_AI 0 0 0 0 0 0
CalculatedCreditInfo 243 257 9 NULL -1 0 0 NULL 0 1 0 0 0 1
udt_QoutaDetail 243 258 21 NULL -1 0 0 NULL 0 1 0 0 0 1
BeforeUpdate 243 259 22 NULL -1 0 0 NULL 0 1 0 0 0 1
udt_StoreInventory 243 260 26 NULL -1 0 0 NULL 0 1 0 0 0 1
udt_WKFHistory 243 261 32 NULL -1 0 0 NULL 0 1 0 0 0 1
IDTable 243 262 1 NULL -1 0 0 NULL
you can use max_length for size of each data type.
T-SQL has a function for that: DATALENGTH for all SQL Server versions.
Example:
DECLARE #lat DECIMAL(10, 7) = 3.14151415141514151415;
SELECT #lat, DATALENGTH(#lat);
Result:
3.1415142 and 5 (because DECIMAL(10,7) uses 5 bytes to be stored).
Documentation: https://learn.microsoft.com/en-us/sql/t-sql/functions/datalength-transact-sql?view=sql-server-ver15
For example, I have a table called Applications with these columns: (id VARCHAR(32), debug BIT, connectionString VARCHAR(2048), firebaseKey VARCHAR(4096)). As we know, VARCHAR doesn't allocate all the space (just what you need, so 'A' is 1 byte in VARCHAR).
These queries:
SELECT
SUM(DATALENGTH(id)) AS idSize,
SUM(DATALENGTH(debug)) AS debugSize,
SUM(DATALENGTH(connectionString)) AS connectionStringSize,
SUM(DATALENGTH(firebaseKey)) AS firebaseKeySize
FROM Applications;
SELECT
SUM(
DATALENGTH(id) +
DATALENGTH(debug) +
DATALENGTH(connectionString) +
DATALENGTH(firebaseKey)
) AS totalSize
FROM Applications;
will return my data size (in my case, with my rows, is 8, 2, 366, 4698 (total: 5074). There are 2 rows in that table.
Notice that this does NOT represent the total size of my database (there are pages, descriptors, indexes, etc. involved.)*
MSSQL has internal stored procedures to tell you the exactly size of your database in disk:
EXEC sp_spaceused; for all database;
EXEC sp_spaceused N'schema.TableName'; for a specific table;
EXEC sp_helpdb N'DatabaseName'; if you want details from each file.
http://msdn.microsoft.com/en-us/library/ms187752.aspx
Money : 8 bytes
int : 4 bytes
id - depends on what you mean.
If the table specified in the where clause contains a nvarchar, this query will give you how many characters there are for that column correctly!
This detects if the column is "wide" and essentially divides by 2. More broad than just nvarchar.
SELECT c.name, (CASE WHEN LEFT(ts.name, 1) = 'n' AND ts.[precision] = 0 AND ts.[scale] = 0 THEN c.max_length / ts.[bytes] ELSE c.max_length END) AS [length]
FROM sys.columns AS c
INNER JOIN sys.tables AS t
ON t.object_id = c.object_ID
INNER JOIN
(
SELECT *, (CASE WHEN [bits] = -1 THEN -1 ELSE ([bits] + 7) / 8 END) AS [bytes]
FROM (
SELECT *, (CASE WHEN max_length >= 256 THEN (CASE WHEN LEFT(name, 1) = 'n' AND [precision] = 0 AND [scale] = 0 THEN 16 ELSE 8 END) ELSE max_length END) AS [bits]
FROM sys.types AS iits
) AS its
) AS ts
ON ts.user_type_id = c.user_type_id
WHERE t.name LIKE 'tb_tablename' -- LIKE is case insensitive
Of course, you can just divide max_length on sys.columns by 2 if you know the column is an nvarchar. This is more for discovering table schema in a way that seems better for if new sql data types are introduced in the future. And you so-choose to upgrade to it. Pretty small edge case.
Please edit and correct this answer if you find an edge case where
bytes and bits are incorrect.
Details:
-- ([bits] + 7) / 8 means round up
--
-- Proof:
-- o (1 bit + 7 = 8) / 8 = 1 byte used
-- o ((8 + 8 + 1 = 17 bytes) + 7 = 24) / 8 = 3 byes used
-- o ((8 + 8 + 7 = 23 bytes) + 7 = 30) / 8 = 3.75 = integer division removes decimal = 3
SELECT *, (CASE WHEN [bits] = -1 THEN -1 ELSE ([bits] + 7) / 8 END) AS [bytes]
FROM (
SELECT *, (CASE WHEN max_length >= 256 THEN (CASE WHEN LEFT(name, 1) = 'n' AND [precision] = 0 AND [scale] = 0 THEN 16 ELSE 8 END) ELSE max_length END) AS [bits]
FROM sys.types AS its
) AS ts
If someone knows that SQL Server stores the bit and byte sizes for each data type. Or a better way to get sys.columns size, please leave a comment!

Need help to get sql query output like this

This is my query
select
dtfromdate, dttodate,
(SELECT DATEDIFF(day, dtfromdate, dtTodate)) AS NumberOfDays,
fltspl
from dbo.tblHR_SpecialLeaveTransaction
where
nvrempcode = 'MCL1218' and nvrstatus = 1
order by
dtfromdate
Result :
dtfromdate dttodate NumberOfDays fltspl
----------------------- ----------------------- ------------ ----------------------
2012-05-01 00:00:00 2012-05-31 00:00:00 30 30
Another query
select
dtfromdate, dtTodate,
(SELECT DATEDIFF(day, dtfromdate, dtTodate) ) AS NumberOfDays,
fltcl, fltsl, fltpl, fltcompoff, fltod, fltlop,
isnull(fltflexiL, 0) as fltflexiL
from
tblhr_leavetransaction
where
nvrempcode = 'MCL1218' and nvrstatus = 1
order by
dtfromdate
Result:
dtfromdate dtTodate NumberOfDays fltcl fltsl fltpl fltcompoff fltod fltlop fltflexiL
----------------------- ----------------------- ------------ ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ----------------------
2011-01-14 00:00:00 2011-01-14 00:00:00 0 1 0 0 0 0 0 0
2011-01-17 00:00:00 2011-01-17 00:00:00 0 1 0 0 0 0 0 0
2011-01-25 00:00:00 2011-01-25 00:00:00 0 0 0 0 0 1 0 0
2011-04-01 00:00:00 2011-04-02 00:00:00 1 0 0 0 0 2 0 0
2011-05-14 00:00:00 2011-05-14 00:00:00 0 0 0 0 0 1 0 0
2011-05-16 00:00:00 2011-05-16 00:00:00 0 0 0 0 1 0 0 0
2011-05-18 00:00:00 2011-05-18 00:00:00 0 1 0 0 0 0 0 0
2011-05-19 00:00:00 2011-05-20 00:00:00 1 0 2 0 0 0 0 0
2011-05-21 00:00:00 2011-05-21 00:00:00 0 1 0 0 0 0 0 0
2011-05-23 00:00:00 2011-05-23 00:00:00 0 0 0 0 1 0 0 0
I need the output like this,
dtfromdate dtTodate NumberOfDays fltcl fltsl fltpl fltcompoff fltod fltlop fltflexiL fltspl
----------------------- ----------------------- ------------ ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ---------------------- ----------
2011-01-14 00:00:00 2011-01-14 00:00:00 0 1 0 0 0 0 0 0 0
2011-01-17 00:00:00 2011-01-17 00:00:00 0 1 0 0 0 0 0 0 0
2011-01-25 00:00:00 2011-01-25 00:00:00 0 0 0 0 0 1 0 0 0
2011-04-01 00:00:00 2011-04-02 00:00:00 1 0 0 0 0 2 0 0 0
2011-05-14 00:00:00 2011-05-14 00:00:00 0 0 0 0 0 1 0 0 0
2011-05-16 00:00:00 2011-05-16 00:00:00 0 0 0 0 1 0 0 0 0
2011-05-18 00:00:00 2011-05-18 00:00:00 0 1 0 0 0 0 0 0 0
2011-05-19 00:00:00 2011-05-20 00:00:00 1 0 2 0 0 0 0 0 0
2011-05-21 00:00:00 2011-05-21 00:00:00 0 1 0 0 0 0 0 0 0
2011-05-23 00:00:00 2011-05-23 00:00:00 0 0 0 0 1 0 0 0 0
2012-05-01 00:00:00 2012-05-31 00:00:00 30 0 0 0 0 0 0 0 30
Looks like you just want to union two queries together. To do this both queries must include all columns, just set those to null/zero as necessary. One other slight difficulty is that you cant use order by when unioning, unless you subselect the whole thing.
Without the order by:
select
dtfromdate, dttodate,
(SELECT DATEDIFF(day, dtfromdate, dtTodate)) AS NumberOfDays,
0 as fltcl,
0 as fltsl,
0 as fltpl,
0 as fltcompoff,
0 as fltod,
0 as fltlop,
0 as fltflexiL,
fltspl
from dbo.tblHR_SpecialLeaveTransaction
where
nvrempcode = 'MCL1218' and nvrstatus = 1
union
select
dtfromdate, dtTodate,
(SELECT DATEDIFF(day, dtfromdate, dtTodate) ) AS NumberOfDays,
fltcl, fltsl, fltpl, fltcompoff, fltod, fltlop,
isnull(fltflexiL, 0) as fltflexiL ,
0 as fltspl
from
tblhr_leavetransaction
where
nvrempcode = 'MCL1218' and nvrstatus = 1
If you need a specific order:
SELECT * FROM
(
select
dtfromdate, dttodate,
(SELECT DATEDIFF(day, dtfromdate, dtTodate)) AS NumberOfDays,
0 as fltcl,
0 as fltsl,
0 as fltpl,
0 as fltcompoff,
0 as fltod,
0 as fltlop,
0 as fltflexiL,
fltspl
from dbo.tblHR_SpecialLeaveTransaction
where
nvrempcode = 'MCL1218' and nvrstatus = 1
union
select
dtfromdate, dtTodate,
(SELECT DATEDIFF(day, dtfromdate, dtTodate) ) AS NumberOfDays,
fltcl, fltsl, fltpl, fltcompoff, fltod, fltlop,
isnull(fltflexiL, 0) as fltflexiL ,
0 as fltspl
from
tblhr_leavetransaction
where
nvrempcode = 'MCL1218' and nvrstatus = 1
) src
order by dtfromdate

Resources