Like everything in live, one can like or dislike something. One of new feature in Visual Studio 2013 is Peak Definition. This one is something I dislike to be honest. Someone will say, then don’t use it when you don’t like it. That is true, but one of the visual studio extensions “Productivity Power Tools” adds one more way to see the Peek Definition Window. Default shortcut from Visual Studio to access it is Alt + F12, ReSharper adds a shortcut but luckily harder to hit it by mistake Ctrl + Shift + Click, but this extension adds one more which is bothering. The shortcut is Ctrl + Click. That means when you want to open the file where some type is defined, instead of opening, you get the Peek window.
For ones that this shortcut is bothering here is the way how to disable it:
- From Visual Studio open Tools/Options and then navigate to Productivity Power Tools
Sunday, June 29, 2014
Peek Definition (Disable)
Thursday, August 12, 2010
20 Tests that your database design should pass
Recently i discovered a small application named SqlCop that integrates many database tests. I tried the tool and it was pretty much ok, although I’ve discovered some application bugs but also some of the queries(tests) were not working in my environment. So i decided to completely rewrite all those queries, without worrying about older Sql Server versions, and stick only with views from sys schema. Queries are tested on Sql Server 2005 and 2008.
-- Procedures or functions with prefix sp_
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name
, o.type_desc
FROM sys.objects o
WHERE o.is_ms_shipped = 0
AND o.type IN ('P', 'FN', 'TF', 'IF', 'AF', 'FT', 'IS', 'PC', 'FS')
AND LOWER(o.name) LIKE 'sp[_]%'
AND o.name NOT IN ('sp_alterdiagram', 'sp_creatediagram',
'sp_dropdiagram', 'sp_helpdiagramdefinition',
'sp_helpdiagrams', 'sp_renamediagram',
'sp_upgraddiagrams')
ORDER BY Found
-- Tables with prefix tbl
SELECT Found = OBJECT_SCHEMA_NAME(t.object_id) + '.' + t.name
, t.type_desc
FROM sys.tables t
WHERE t.type = 'U'
AND LOWER(t.name) LIKE 'tbl%'
ORDER BY Found
-- VarChar missing size
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name
, o.type_desc
FROM sys.objects o
WHERE o.is_ms_shipped = 0
AND o.type IN ('P', 'FN', 'TF', 'IF', 'AF', 'FT', 'IS', 'PC', 'FS')
AND REPLACE(REPLACE(REPLACE(LOWER(OBJECT_DEFINITION(o.object_id)), ' ', ''), ' ', ''), ']', '') LIKE '%varchar[^(]%'
AND o.name NOT LIKE 'SqlQueryNotificationStoredProcedure-%'
ORDER BY Found
-- Decimal missing size
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name
, o.type_desc
FROM sys.objects o
WHERE o.is_ms_shipped = 0
AND o.type IN ('P', 'FN', 'TF', 'IF', 'AF', 'FT', 'IS', 'PC', 'FS')
AND (REPLACE(REPLACE(REPLACE(LOWER(OBJECT_DEFINITION(o.object_id)), ' ', ''), ' ', ''), ']', '') LIKE '%decimal[^(]%'
OR
REPLACE(REPLACE(REPLACE(LOWER(OBJECT_DEFINITION(o.object_id)), ' ', ''), ' ', ''), ']', '') LIKE '%numeric[^(]%'
)
AND o.name NOT LIKE 'SqlQueryNotificationStoredProcedure-%'
ORDER BY Found
-- Using undocumented procedures
DECLARE @Temp TABLE(ProcedureName VARCHAR(50))
INSERT INTO @Temp VALUES('sp_MStablespace')
INSERT INTO @Temp VALUES('sp_who2')
INSERT INTO @Temp VALUES('sp_tempdbspace')
INSERT INTO @Temp VALUES('sp_MSkilldb')
INSERT INTO @Temp VALUES('sp_MSindexspace')
INSERT INTO @Temp VALUES('sp_MShelptype')
INSERT INTO @Temp VALUES('sp_MShelpindex')
INSERT INTO @Temp VALUES('sp_MShelpcolumns')
INSERT INTO @Temp VALUES('sp_MSforeachtable')
INSERT INTO @Temp VALUES('sp_MSforeachdb')
INSERT INTO @Temp VALUES('sp_fixindex')
INSERT INTO @Temp VALUES('sp_columns_rowset')
INSERT INTO @Temp VALUES('sp_MScheck_uid_owns_anything')
INSERT INTO @Temp VALUES('sp_MSgettools_path')
INSERT INTO @Temp VALUES('sp_gettypestring')
INSERT INTO @Temp VALUES('sp_MSdrop_object')
INSERT INTO @Temp VALUES('sp_MSget_qualified_name')
INSERT INTO @Temp VALUES('sp_MSgetversion')
INSERT INTO @Temp VALUES('xp_dirtree')
INSERT INTO @Temp VALUES('xp_subdirs')
INSERT INTO @Temp VALUES('xp_enum_oledb_providers')
INSERT INTO @Temp VALUES('xp_enumcodepages')
INSERT INTO @Temp VALUES('xp_enumdsn')
INSERT INTO @Temp VALUES('xp_enumerrorlogs')
INSERT INTO @Temp VALUES('xp_enumgroups')
INSERT INTO @Temp VALUES('xp_fileexist')
INSERT INTO @Temp VALUES('xp_fixeddrives')
INSERT INTO @Temp VALUES('xp_getnetname')
INSERT INTO @Temp VALUES('xp_readerrorlog')
INSERT INTO @Temp VALUES('sp_msdependencies')
INSERT INTO @Temp VALUES('xp_qv')
INSERT INTO @Temp VALUES('xp_delete_file')
INSERT INTO @Temp VALUES('sp_checknames')
INSERT INTO @Temp VALUES('sp_enumoledbdatasources')
INSERT INTO @Temp VALUES('sp_MS_marksystemobject')
INSERT INTO @Temp VALUES('sp_MSaddguidcolumn')
INSERT INTO @Temp VALUES('sp_MSaddguidindex')
INSERT INTO @Temp VALUES('sp_MSaddlogin_implicit_ntlogin')
INSERT INTO @Temp VALUES('sp_MSadduser_implicit_ntlogin')
INSERT INTO @Temp VALUES('sp_MSdbuseraccess')
INSERT INTO @Temp VALUES('sp_MSdbuserpriv')
INSERT INTO @Temp VALUES('sp_MSloginmappings')
INSERT INTO @Temp VALUES('sp_MStablekeys')
INSERT INTO @Temp VALUES('sp_MStablerefs')
INSERT INTO @Temp VALUES('sp_MSuniquetempname')
INSERT INTO @Temp VALUES('sp_MSuniqueobjectname')
INSERT INTO @Temp VALUES('sp_MSuniquecolname')
INSERT INTO @Temp VALUES('sp_MSuniquename')
INSERT INTO @Temp VALUES('sp_MSunc_to_drive')
INSERT INTO @Temp VALUES('sp_MSis_pk_col')
INSERT INTO @Temp VALUES('xp_get_MAPI_default_profile')
INSERT INTO @Temp VALUES('xp_get_MAPI_profiles')
INSERT INTO @Temp VALUES('xp_regdeletekey')
INSERT INTO @Temp VALUES('xp_regdeletevalue')
INSERT INTO @Temp VALUES('xp_regread')
INSERT INTO @Temp VALUES('xp_regenumvalues')
INSERT INTO @Temp VALUES('xp_regaddmultistring')
INSERT INTO @Temp VALUES('xp_regremovemultistring')
INSERT INTO @Temp VALUES('xp_regwrite')
INSERT INTO @Temp VALUES('xp_varbintohexstr')
INSERT INTO @Temp VALUES('sp_MSguidtostr')
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name
, o.type_desc
FROM sys.objects o
INNER Join @Temp T ON LOWER(OBJECT_DEFINITION(o.object_id)) Like '%' + LOWER(T.ProcedureName) + '%'
WHERE o.is_ms_shipped = 0
AND o.type IN ('P', 'FN', 'TF', 'IF', 'AF', 'FT', 'IS', 'PC', 'FS')
AND o.name NOT LIKE 'SqlQueryNotificationStoredProcedure-%'
ORDER BY Found
-- Procedures without SET NOCOUNT ON
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name
, o.type_desc
FROM sys.objects o
WHERE o.is_ms_shipped = 0
AND o.type = 'P'
AND UPPER(OBJECT_DEFINITION(o.object_id)) NOT LIKE '%SET NOCOUNT ON%'
AND o.name NOT LIKE 'SqlQueryNotificationStoredProcedure-%'
ORDER BY Found
-- Procedures with SET ROWCOUNT
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name
, o.type_desc
FROM sys.objects o
WHERE o.is_ms_shipped = 0
AND o.type = 'P'
AND UPPER(OBJECT_DEFINITION(o.object_id)) LIKE '%SET ROWCOUNT%'
AND o.name NOT LIKE 'SqlQueryNotificationStoredProcedure-%'
ORDER BY Found
-- Procedures with @@identity
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name
, o.type_desc
FROM sys.objects o
WHERE o.is_ms_shipped = 0
AND o.type = 'P'
AND o.name <> 'sp_creatediagram'
AND LOWER(OBJECT_DEFINITION(o.object_id)) LIKE '%@@identity%'
AND o.name NOT LIKE 'SqlQueryNotificationStoredProcedure-%'
ORDER BY Found
-- Invalid characters in column or object names
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name
, o.type_desc
FROM sys.objects o
WHERE o.is_ms_shipped = 0
AND LOWER(o.name) LIKE '%[^a-z0-9_.-]%'
AND o.name NOT LIKE 'SqlQueryNotificationStoredProcedure-%'
UNION ALL
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name + '.' + c.name
, type_desc = 'COLUMN'
FROM sys.columns c
INNER JOIN sys.objects o ON c.object_id = o.object_id
WHERE o.is_ms_shipped = 0
AND LOWER(c.name) LIKE '%[^a-z0-9_.-]%'
ORDER BY Found
-- Using of float or real data types
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name
, o.type_desc
FROM sys.objects o
WHERE o.is_ms_shipped = 0
AND o.name <> 'sp_creatediagram'
AND o.name NOT LIKE 'SqlQueryNotificationStoredProcedure-%'
AND (LOWER(OBJECT_DEFINITION(o.object_id)) LIKE '%float%'
OR
LOWER(OBJECT_DEFINITION(o.object_id)) LIKE '%real%')
UNION ALL
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name + '.' + c.name
, type_desc = 'COLUMN'
FROM sys.columns c
INNER JOIN sys.objects o ON c.object_id = o.object_id
INNER JOIN sys.types t ON c.system_type_id = t.system_type_id
WHERE o.is_ms_shipped = 0
AND t.name IN ('float', 'real')
ORDER BY FOUND
-- Using of text, ntext or image data types
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name
, o.type_desc
FROM sys.objects o
WHERE o.is_ms_shipped = 0
AND o.name <> 'sp_creatediagram'
AND o.name NOT LIKE 'SqlQueryNotificationStoredProcedure-%'
AND (LOWER(OBJECT_DEFINITION(o.object_id)) LIKE '%text%'
OR
LOWER(OBJECT_DEFINITION(o.object_id)) LIKE '%ntext%'
OR
LOWER(OBJECT_DEFINITION(o.object_id)) LIKE '%image%')
UNION ALL
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name + '.' + c.name
, type_desc = 'COLUMN'
FROM sys.columns c
INNER JOIN sys.objects o ON c.object_id = o.object_id
INNER JOIN sys.types t ON c.system_type_id = t.system_type_id
WHERE o.is_ms_shipped = 0
AND t.name IN ('text', 'ntext', 'image')
ORDER BY FOUND
-- Collation problem. Column's collation is different from database collation
SELECT Found = OBJECT_SCHEMA_NAME(o.object_id) + '.' + o.name + '.' + c.name
, type_desc = 'COLUMN'
FROM sys.columns c
INNER JOIN sys.objects o ON c.object_id = o.object_id
WHERE o.is_ms_shipped = 0
AND CAST(c.collation_name AS VARCHAR(50)) <> CAST(DATABASEPROPERTYEX(DB_NAME(), 'Collation') AS VARCHAR(50))
-- Empty tables
SET NOCOUNT ON ;
CREATE TABLE #EmptyTables (Table_Name VARCHAR(100))
EXEC sp_msforeachtable 'If Not Exists(Select * From ?)
Insert Into #EmptyTables Values(''?'')'
SELECT Table_Name
FROM #EmptyTables
ORDER BY Table_Name
DROP TABLE #EmptyTables
-- Tables without primary key
SELECT Found = OBJECT_SCHEMA_NAME(t.object_id) + '.' + t.name
, type_desc = 'PRIMARY_KEY'
FROM sys.tables t
LEFT OUTER JOIN (SELECT object_id FROM sys.indexes WHERE is_primary_key = 1) i ON t.object_id = i.object_id
WHERE i.object_id IS NULL
-- Tables without clustered index
SELECT Found = OBJECT_SCHEMA_NAME(t.object_id) + '.' + t.name
, type_desc = 'TABLE'
FROM sys.tables t
LEFT OUTER JOIN (SELECT object_id FROM sys.indexes WHERE type = 1) i ON t.object_id = i.object_id
WHERE i.object_id IS NULL
ORDER BY Found
-- Missing foreign Keys
-- If a column ends with id but is not a foreign key, it should be renamed
SELECT Found = OBJECT_SCHEMA_NAME(t.object_id) + '.' + t.name + '.' + c.name
, type_desc = 'FOREIGN_KEY_CONSTRAINT'
FROM sys.tables t
INNER JOIN sys.columns c ON t.object_id = c.object_id
LEFT OUTER JOIN (SELECT fk.parent_object_id, fkc.parent_column_id
FROM sys.foreign_keys fk
INNER JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id
) fkc ON c.object_id = fkc.parent_object_id AND c.column_id = fkc.parent_column_id
LEFT OUTER JOIN (SELECT ic.object_id, ic.column_id
FROM sys.index_columns ic
INNER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
INNER JOIN (SELECT object_id
FROM sys.index_columns
GROUP BY object_id, index_id
HAVING COUNT(*) = 1) icu ON ic.object_id = icu.object_id
WHERE i.is_primary_key = 1
) ic ON c.object_id = ic.object_id AND c.column_id = ic.column_id
WHERE fkc.parent_column_id IS Null
AND ic.object_id IS NULL
AND t.name <> 'sysdiagrams'
AND t.name NOT LIKE '%Log'
AND t.name NOT LIKE 'Log%'
AND c.name Like '%Id'
ORDER BY FOUND
-- Uniqueidentifier column used in clustered index as first column and have default value newid()
-- check for default value is commented to show all clustered indexes with first column with uniqueidentifier type
-- it is posible that having such first column and that to be fine if that is a foreign key column.
-- But if you set a default values on all such columns to newid(), even so you set their values in insert procedure,
-- then you can uncomment the last check, and with that remove the ones that are fine as described.
SELECT Found = OBJECT_SCHEMA_NAME(t.object_id) + '.' + t.name + ': ' + i.name + ' (' + c.name + ')'
, type_desc = 'CLUSTERED_INDEX'
FROM sys.index_columns ic
INNER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
INNER JOIN sys.tables t ON i.object_id = t.object_id
INNER JOIN sys.columns c ON t.object_id = c.object_id AND ic.column_id = c.column_id
INNER JOIN sys.types tp ON c.system_type_id = tp.system_type_id
WHERE i.type = 1
AND ic.column_id = 1
AND tp.name = 'uniqueidentifier'
--AND c.default_object_id <> 0
ORDER BY Found
-- Missing index for foreign key columns
-- It is posible that creating indexes for all foreign keys is not required. So you should decide for which ones to create indexes.
SET NOCOUNT ON ;
CREATE TABLE #SmallTables (object_id int)
EXEC sp_msforeachtable 'If Not Exists(SELECT * FROM (Select Rows = Count(*) From ?) R WHERE Rows < 100)
Insert Into #SmallTables Values(OBJECT_ID(''?''))'
SELECT Found = OBJECT_SCHEMA_NAME(t.object_id) + '.' + t.name + '.' + fkc.name + ' (' + c.name + ')'
, type_desc = 'INDEX'
, Resolution = 'CREATE NONCLUSTERED INDEX [IX_' + t.name + '_' + c.name + '] ON [' + OBJECT_SCHEMA_NAME(t.object_id) + '].[' + t.name + ']([' + c.name + '])'
FROM sys.tables t
INNER JOIN sys.columns c ON t.object_id = c.object_id
INNER JOIN (SELECT fk.parent_object_id, fkc.parent_column_id, fk.name
FROM sys.foreign_keys fk
INNER JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id
) fkc ON c.object_id = fkc.parent_object_id AND c.column_id = fkc.parent_column_id
LEFT OUTER JOIN (SELECT ic.object_id, ic.column_id
FROM sys.index_columns ic
INNER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
INNER JOIN sys.index_columns icu ON ic.object_id = icu.object_id
WHERE ic.index_column_id = 1
) ic ON fkc.parent_object_id = ic.object_id AND fkc.parent_column_id = ic.column_id
-- INNER JOIN sys.types tp ON c.system_type_id = tp.system_type_id
INNER JOIN #SmallTables st ON t.object_id = st.object_id
WHERE ic.object_id IS NULL
AND t.name <> 'sysdiagrams'
AND t.name NOT LIKE '%Log'
AND t.name NOT LIKE 'Log%'
--AND tp.name <> 'uniqueidentifier'
ORDER BY FOUND
DROP TABLE #SmallTables
-- Wide tables
SELECT Found = OBJECT_SCHEMA_NAME(t.object_id) + '.' + OBJECT_NAME(t.object_id)
, type_desc = 'TABLE'
FROM sys.tables t
INNER JOIN sys.columns c ON t.object_id = c.object_id
INNER JOIN sys.types tp ON c.system_type_id = tp.system_type_id
GROUP BY t.object_id
HAVING SUM(ISNULL(NULLIF(CONVERT(BIGINT, tp.max_length), 8000), 0) + CASE WHEN c.collation_name IS NULL THEN 0 ELSE c.max_length END) > 8060
-- Missing index
;WITH missing_index_impact AS (
SELECT dm_db_missing_index_groups.index_handle,
SUM(( dm_db_missing_index_group_stats.avg_total_user_cost
* dm_db_missing_index_group_stats.avg_user_impact
* ( dm_db_missing_index_group_stats.user_seeks
+ dm_db_missing_index_group_stats.user_scans ) )) AS "total_impact",
( 100.00
* SUM(dm_db_missing_index_group_stats.avg_total_user_cost
* dm_db_missing_index_group_stats.avg_user_impact
* ( dm_db_missing_index_group_stats.user_seeks
+ dm_db_missing_index_group_stats.user_scans ))
/ SUM(SUM(dm_db_missing_index_group_stats.avg_total_user_cost
* dm_db_missing_index_group_stats.avg_user_impact
* ( dm_db_missing_index_group_stats.user_seeks
+ dm_db_missing_index_group_stats.user_scans ))) OVER ( ) ) AS "percent_impact",
ROW_NUMBER() OVER ( ORDER BY SUM(avg_total_user_cost
* avg_user_impact
* ( user_seeks
+ user_scans )) DESC ) AS rn
FROM sys.dm_db_missing_index_groups AS dm_db_missing_index_groups
JOIN sys.dm_db_missing_index_group_stats AS dm_db_missing_index_group_stats ON dm_db_missing_index_groups.index_group_handle = dm_db_missing_index_group_stats.group_handle
GROUP BY dm_db_missing_index_groups.index_handle
),
agg_missing_index_impact AS (
SELECT missing_index_impact_1.index_handle,
missing_index_impact_1.total_impact,
SUM(missing_index_impact_2.total_impact) AS running_total_impact,
missing_index_impact_1.percent_impact,
SUM(missing_index_impact_2.percent_impact) AS running_total_percent,
missing_index_impact_1.rn
FROM missing_index_impact AS missing_index_impact_1
JOIN missing_index_impact AS missing_index_impact_2 ON missing_index_impact_1.rn <= missing_index_impact_2.rn
GROUP BY missing_index_impact_1.index_handle,
missing_index_impact_1.total_impact,
missing_index_impact_1.percent_impact,
missing_index_impact_1.rn
),
missing_index_details AS (
SELECT dm_db_missing_index_details.database_id,
dm_db_missing_index_details."object_id",
dm_db_missing_index_details.index_handle,
dm_db_missing_index_details."statement",
dm_db_missing_index_details.equality_columns,
dm_db_missing_index_details.inequality_columns,
dm_db_missing_index_details.included_columns
FROM sys.dm_db_missing_index_details AS dm_db_missing_index_details
)
SELECT agg_missing_index_impact.rn,
missing_index_details."statement",
missing_index_details.equality_columns,
missing_index_details.inequality_columns,
missing_index_details.included_columns,
agg_missing_index_impact.running_total_impact,
agg_missing_index_impact.total_impact,
agg_missing_index_impact.running_total_percent,
agg_missing_index_impact.percent_impact,
"key".index_key_column_count,
"key".index_key_column_bytes,
"all".index_all_column_count,
"all".index_all_column_bytes
FROM agg_missing_index_impact AS agg_missing_index_impact
JOIN missing_index_details AS missing_index_details ON agg_missing_index_impact.index_handle = missing_index_details.index_handle
JOIN ( SELECT missing_index_details1.index_handle,
COUNT(*) AS index_key_column_count,
SUM(COL_LENGTH(missing_index_details1."statement",
dm_db_missing_index_columns1.column_name)) AS index_key_column_bytes
FROM missing_index_details AS missing_index_details1
CROSS APPLY sys.dm_db_missing_index_columns(missing_index_details1.index_handle) AS dm_db_missing_index_columns1
WHERE dm_db_missing_index_columns1.column_usage = 'EQUALITY'
OR dm_db_missing_index_columns1.column_usage = 'INEQUALITY'
GROUP BY missing_index_details1.index_handle
) AS "key" ON missing_index_details.index_handle = "key".index_handle
JOIN ( SELECT missing_index_details2.index_handle,
COUNT(*) AS index_all_column_count,
SUM(COL_LENGTH(missing_index_details2."statement",
dm_db_missing_index_columns2.column_name)) AS index_all_column_bytes
FROM missing_index_details AS missing_index_details2
CROSS APPLY sys.dm_db_missing_index_columns(missing_index_details2.index_handle) AS dm_db_missing_index_columns2
GROUP BY missing_index_details2.index_handle
) AS "all" ON missing_index_details.index_handle = "all".index_handle
--WHERE missing_index_details.database_id = ?
ORDER BY agg_missing_index_impact.rn ASC ;
Tuesday, June 22, 2010
Calculate DateTime difference in SQL Server with ITVF
To return to the title and write functions that are very useful in any database. I will present two just to give you idea, from which you can create some others. First the function for calculating Years difference between two dates:
CREATE FUNCTION dbo.DateDiffYears
(
@DateFrom AS DATETIME
, @DateTo AS DATETIME
)
RETURNS TABLE
AS
RETURN SELECT Years = DATEDIFF(yy, @DateFrom, @DateTo)
- CASE WHEN DATEPART(dy, @DateFrom) > DATEPART(dy, @DateTo)
THEN 1 ELSE 0 END
Now you can use with CROSS APPLY in your queries. Imagine a table Persons that have a column BirthDate. You need to return their age in the result set. You will have something like this:
SELECT PersonId
, Age = DY.Years
FROM Persons
CROSS APPLY dbo.DateDiffYears(BirthDate, GETDATE())
The second one is called DateSpan, not like .NET TimeSpan but if you need the same one, you can easely create one, which will return Years, Months and Days difference between two days:
CREATE FUNCTION dbo.DateSpan
(
@DateFrom AS DATETIME
, @DateTo AS DATETIME
)
RETURNS TABLE
AS
RETURN SELECT Years, TotalMonths, Months = TotalMonths - Years * 12, TotalDays, [Days]
FROM (
Years = DATEDIFF(yy, @DateFrom, @DateTo) - CASE WHEN DATEPART(dy, @DateFrom) > DATEPART(dy, @DateTo) THEN 1 ELSE 0 END
, TotalMonths = DATEDIFF(mm, @DateFrom, @DateTo) - CASE WHEN DATEPART(d, @DateFrom) > DATEPART(d, @DateTo) THEN 1 ELSE 0 END
, TotalDays = DATEDIFF(dd, @DateFrom, @DateTo)
, [Days] = ABS(DAY(@DateTo) - DAY(@DateFrom) + DAY(DATEADD(d, -DAY(@DateTo), @DateTo)))
) D
The performance difference between ITVF and scalar functions is big and lineary grows on number of rows, and you will not see any additional row in the profiler.
Wednesday, July 2, 2008
Calculate DateTime Difference in SQL Server
I have no idea, why still SQL Server have no support for TimeSpan type. Also in 2008 version will be so many new DateTime types but still functions for manipulating DateTime values are few. If you need to calculate difference between two dates, but that difference to be represented as years:months: days, you will realize that that is not an easy task. There is DateDiff function, but when you try to use it you will see that this function will not return desired result. At least to get the result in a easy way.
I was in a need for such a function so I start looking for on the net. I found one which was pretty much what i needed. However I decide to create my own, much simpler and just for what I need. So here it is:
CREATE FUNCTION [dbo].[TotalDateDiff]
(
@DateFrom AS SMALLDATETIME,
@DateTo AS SMALLDATETIME
)
RETURNS CHAR(8)
AS
BEGIN
DECLARE @Result CHAR(8)
DECLARE @Years SMALLINT, @Months SMALLINT, @Days SMALLINT
SELECT @Years = DATEDIFF(YEAR, @DateFrom, @DateTo),
@Months = DATEDIFF(MONTH, @DateFrom, @DateTo)
SET @Months = @Months - @Years * 12
SET @Days = DAY(@DateTo) - DAY(@DateFrom)
IF @Days < 0
BEGIN
SET @Months = @Months - 1
SET @Days = DAY(DATEADD(DAY, -DAY(@DateTo), @DateTo)) + @Days
END
IF @Months < 0
BEGIN
SET @Years = @Years - 1
SET @Months = 12 + @Months
END
SET @Result = RIGHT('0' + CAST(@Years AS VARCHAR(2)), 2) + ':' +
RIGHT('0' + CAST(@Months AS VARCHAR(2)), 2) + ':' +
RIGHT('0' + CAST(@Days AS VARCHAR(2)), 2)
RETURN @Result
END
Tuesday, July 1, 2008
Visual C# MVP Award
Today I receive mail from Microsoft:
Congratulations! We are pleased to present you with the 2008 Microsoft® MVP Award! The MVP Award is our way to say thank you for promoting the spirit of community and improving people’s lives and the industry’s success every day. We appreciate your extraordinary efforts in Visual C# technical communities during the past year.
Thanks Microsoft, I really appreciate this award.
My efforts in past couple of years on msdn forums, in particular C#, ADO, SQL forums, for helping other developers is the main reason. My nickname is boban.s, so you can find some very useful posts from ones about base class libraries in .NET though posts related with windows application type of problems to threading, localization, application update etc.
You probably know that winning MVP award is a result of many activities in public community, writing books, blogs, managing user groups, etc. I would like to make this blog active in next year in order to get award for next year too. I will post mainly about C#, but also about T-SQL and SharePoint. I already have in mind what will be the next post.
Thursday, April 10, 2008
SMA Technical Indicator
public class SMA
{
private readonly int _Length;
private readonly bool _StoreData;
private decimal _Value;
private decimal _Price;
private bool _Primed;
private readonly string _Name;
private readonly DecimalCollection _PriceArray = new DecimalCollection();
private readonly DecimalCollection _ValueArray = new DecimalCollection();
public SMA(int length) : this(length, false)
{
}
/// <summary>
/// Class for calculating Simple Moving Average
/// </summary>
/// <param name="length">Lenght SMA calculation formula</param>
/// <param name="storeData"></param>
public SMA(int length, bool storeData)
{
_Length = length;
_StoreData = storeData;
_Name = GetType().Name + Length;
}
public int Length
{
get { return _Length; }
}
public bool StoreData
{
get { return _StoreData; }
}
public decimal Value
{
get { return _Value; }
}
public bool Primed
{
get { return _Primed; }
}
public string Name
{
get { return _Name; }
}
public DecimalCollection ValueArray
{
get { return _ValueArray; }
}
public void PriceTick(decimal price, bool add)
{
if (add)
AddPrice(price);
else
EditPrice(price);
}
public void PriceTicks(DecimalCollection prices)
{
if (prices == null || prices.Count == 0)
return;
for (int i = 0; i < prices.Count; i++)
{
AddPrice(prices[i]);
}
}
private void AddPrice(decimal price)
{
_Price = price;
if (!_Primed)
{
_PriceArray.Add(_Price);
if (_PriceArray.Count == Length)
{
_Primed = true;
_Value = _PriceArray.Average();
}
}
else
{
_PriceArray.RemoveAt(0);
_PriceArray.Add(price);
_Value = _PriceArray.Average();
if (_StoreData)
{
ValueArray.Add(_Value);
}
}
}
private void EditPrice(decimal price)
{
if (price != _Price)
{
_Price = price;
_PriceArray[_PriceArray.Count - 1] = _Price;
if (_Primed)
{
if (_PriceArray.Count == _Length)
{
_Value = _PriceArray.Average();
if (_StoreData)
{
ValueArray[ValueArray.Count - 1] = _Value;
}
}
else
{
_Value = _PriceArray.Average();
if (_StoreData)
{
ValueArray[ValueArray.Count - 1] = _Value;
}
}
}
}
}
}
This source uses DecimalCollection class that is already published on my blog.
Friday, May 18, 2007
Password Generator
public static string GeneratePassword(int passwordLen, bool includeSmallLetters, bool includeBigLetters,
bool includeNumbers, bool includeSpecialCharacters)
{
char[] returnValue = new char[passwordLen];
char[] Choises;
string smallLetters = string.Empty;
string bigLetters = string.Empty;
string numbers = string.Empty;
string specialcharacters = string.Empty;
int choisesLen = 0;
if (includeSmallLetters)
{
smallLetters = "abcdefghijklmnopqrstuvwxyz";
choisesLen += smallLetters.Length;
}
if (includeBigLetters)
{
bigLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
choisesLen += bigLetters.Length;
}
if (includeNumbers)
{
numbers = "012345678901234567890123456789";
choisesLen += numbers.Length;
}
if (includeSpecialCharacters)
{
specialcharacters = "~`!@#$%^&*()-_+=\|<,>.?/ {[}]";
choisesLen += specialcharacters.Length;
}
if (choisesLen == 0)
throw new ArgumentOutOfRangeException("includeSmallLetters",
"At least one type of characters must be included!");
Choises = (smallLetters + bigLetters + numbers + specialcharacters).ToCharArray();
Random rnd = new Random();
for (int i = 0; i < passwordLen; i++)
{
returnValue[i] = Choises[rnd.Next(choisesLen - 1)];
}
return new string(returnValue);
}