Monday, 17 June 2013

.NET - Extend ListControl with GetSelectedItems and GetSelectedValues

Sometimes we need to get from ListBox or CheckBoxList the all selected items. Unfortunately there is only one ready to use function: GetSelectedIndices().
With the help of a very simple extensions we can add two additional functions, GetSelectedItems() and GetSelectedValues():
  1. using System.Collections.Generic;
  2. using System.Linq;
  3. using System.Web.UI.WebControls;
  4.  
  5. namespace TypeExtensions
  6. {
  7. // ReSharper disable InconsistentNaming
  8. public static class System_Web_UI_ListControl
  9. // ReSharper restore InconsistentNaming
  10. {
  11. /// <summary>
  12. /// Returns ListControl selected items.
  13. /// </summary>
  14. public static List<ListItem> GetSelectedItems(this ListControl listControl)
  15. {
  16. var selectedItems = listControl.Items
  17. .OfType<ListItem>()
  18. .Where(listItem => listItem.Selected)
  19. .ToList();
  20. return selectedItems;
  21. }
  22.  
  23. /// <summary>
  24. /// Returns ListControl selected values.
  25. /// </summary>
  26. public static List<string> GetSelectedValues(this ListControl listControl)
  27. {
  28. var selectedItems = listControl.GetSelectedItems();
  29. var selectedValues = selectedItems
  30. .Select(listItem => listItem.Value)
  31. .ToList();
  32. return selectedValues;
  33. }
  34. }
  35. }

Simply add Namespace of this class to your namespace usages and you will become two additional functions:


These extension functions are working for all ListControl-based classes.

Thursday, 13 June 2013

MSSQL - Query Templates - usage of system tables

Sometimes you have to find all the tables (their names), which have the field with the specified name.
The easiest way to do this is through queries to the system tables.
Because this task occurs frequently, I have created T-SQL template:
  1. -- Find Tables by Column-Name
  2. USE <DataBaseName, sysname, tempdb>;
  3.  
  4. SELECT
  5. [SCHEMA] = schemas.name,
  6. [TABLE] = tables.name,
  7. [COLUMN] = columns.name
  8. FROM
  9. sys.schemas
  10. INNER JOIN sys.tables ON
  11. schemas.schema_id = tables.schema_id
  12. INNER JOIN sys.columns ON
  13. tables.object_id = columns.object_id
  14. WHERE
  15. schemas.name IN ('<schema_name, sysname, dbo>')
  16. AND
  17. tables.name LIKE '<TableNamePattern, sysname, %>'
  18. AND
  19. columns.name LIKE '<ColumnNamePattern, sysname, %_ID>'
  20. ORDER BY
  21. [SCHEMA],
  22. [TABLE],
  23. [COLUMN];
Save this template to your templates folder with for example following name:
"SysTables - Sch-Tb-Clm". After replacing of placeholders you will see something like this:
  1. -- Find Dimension-Tables with Code-Columns
  2. USE AdventureWorksDW2008R2;
  3.  
  4. SELECT
  5. [SCHEMA] = schemas.name,
  6. [TABLE] = tables.name,
  7. [COLUMN] = columns.name
  8. FROM
  9. sys.schemas
  10. INNER JOIN sys.tables ON
  11. schemas.schema_id = tables.schema_id
  12. INNER JOIN sys.columns ON
  13. tables.object_id = columns.object_id
  14. WHERE
  15. schemas.name IN ('dbo')
  16. AND
  17. tables.name LIKE 'Dim%'
  18. AND
  19. columns.name LIKE '%Code'
  20. ORDER BY
  21. [SCHEMA],
  22. [TABLE],
  23. [COLUMN];

The result of execution will be something like this:

MSSQL - Query Templates

Often working with the database we have to write lot of typical queries.
In this case it is convenient to create templates of such requests.
SQL Server Management Studio (SSMS) gives us that opportunity.

First of all, make sure that you have activated the tab "Templates Explorer":
[Menu] - [View] - [Template Explorer] or <Control>+<Alt>+<T>:


SSMS will show us lot of ready-to-use templates:



For example in the folder "SQL Server Templates" - "Stored Procedure" template "Create Procedure Basic Template". Click on it with double click or click with right mouse button and select from the context menu item "Open". SMS will open a new T-SQL Scripting window with this text:


In this script, there are some expressions in the form of <Name, DataType, DefaultValue>.
You can fill/replace these placeholders with help of corresponding dialog.
Just call a [Menu]-[Query]-[Specify Values for Template Parameters...]:


or press the button on the toolbar:

In the dialog, you can set new values ​​for the placeholders or leave the default values:


After you click [OK], the placeholders will be replaced with the appropriate values​​:



You can extend templates directory with your custom templates. Just call a context menu for the appropriate folder:


Or copy the folder with your templates (for example @inovex) in this folder:
C:\Users\<Your User>\AppData\Roaming\Microsoft\Microsoft SQL Server\<MSSQL Version>\Tools\Shell\Templates\Sql\



In future articles, I will show examples of some useful templates.

Tuesday, 11 June 2013

MSSQL - Function to format detailed error message

  1. -- Drop Function if exists
  2. IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.fnGetDetailedErrorMessage'))
  3. DROP FUNCTION dbo.fnGetDetailedErrorMessage;
  4.  
  5. GO
  6. /*=============================================
  7. Author:
  8. Yuri Abele
  9. Changes:
  10. 11.06.2013 - Yuri Abele - initial
  11. Description:
  12. Function to format last error as detailed message
  13.  
  14. Usage: Log last error
  15. BEGIN TRY
  16. DECLARE @i INT;
  17. PRINT 'Try division by zero'
  18. SET @i = 1/0;
  19. PRINT 'After'
  20. END TRY
  21. BEGIN CATCH
  22. -- Display error
  23. PRINT dbo.fnGetDetailedErrorMessage();
  24. IF @@TRANCOUNT > 0 ROLLBACK;
  25. END CATCH;
  26. =============================================*/
  27. CREATE FUNCTION dbo.fnGetDetailedErrorMessage()
  28. RETURNS NVARCHAR(MAX)
  29. AS
  30. BEGIN
  31. RETURN ('ERROR:
  32. ErrorNumber = ' + CAST(ERROR_NUMBER() AS VARCHAR(20)) + ',
  33. ErrorSeverity = ' + CAST(ERROR_SEVERITY() AS VARCHAR(20)) + ',
  34. ErrorState = ' + CAST(ERROR_STATE() AS VARCHAR(20)) + ',
  35. ErrorProcedure = "' + ISNULL(ERROR_PROCEDURE(), '') + ',
  36. ErrorLine = ' + CAST(ERROR_LINE() AS VARCHAR(20)) + ',
  37. ErrorMessage = "' + ERROR_MESSAGE() + '"' + CHAR(13) + CHAR(10));
  38. END;
  39. GO
  40.  
  41. BEGIN TRY
  42. DECLARE @i INT;
  43. PRINT 'Try division by zero'
  44. SET @i = 1/0;
  45. PRINT 'After'
  46. END TRY
  47. BEGIN CATCH
  48. -- Display error
  49. PRINT dbo.fnGetDetailedErrorMessage();
  50. IF @@TRANCOUNT > 0 ROLLBACK;
  51. END CATCH;

Results of execution:

MSSQL - Convert list to table #2 (array of string items)

Often, when working with databases there is a need to pass a parameter with set of elements.
Function described here (TABLE-Value UDF) converts a string in the form "aaa;bbb;ccc" to tabular form.
Additionally could be removed empty items.
In case if list has non-unique items, the function returns the serial number for each items group (RANK).
  1. -- Drop Function if exists
  2. IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'dbo.tf_array2table'))
  3. DROP FUNCTION dbo.tf_array2table;
  4. GO
  5. /*=============================================
  6. Author:
  7. Yuri Abele
  8. Changes:
  9. 11.06.2013 - Yuri Abele - initial
  10. Description:
  11. Function to convert list of Values to Table
  12. Usage: Get all items
  13. SELECT item_index, item_value = IIF(item_value = '', '---', item_value), item_rank
  14. FROM dbo.tf_array2table(N';ddd;bbb;ccc;;;ccc;ddd;eee;', N';', 0);
  15. Usage: Get all non-empty items
  16. SELECT * FROM dbo.tf_array2table(N';ddd;bbb;ccc;;;ccc;ddd;eee;', N';', 1);
  17. Usage: Get all non-empty items and filter non-unique
  18. SELECT * FROM dbo.tf_array2table(N';ddd;bbb;ccc;;;ccc;ddd;eee;', N';', 1)
  19. WHERE item_rank=1;
  20. =============================================*/
  21. CREATE FUNCTION dbo.tf_array2table
  22. (
  23. @array NVARCHAR(MAX), -- List of delimited values
  24. @delim NCHAR(1), -- Delimiter
  25. @remove_empty BIT -- Flag to remove empty values
  26. )
  27. -- Container for Array Items
  28. RETURNS @data TABLE(
  29. item_index INT IDENTITY(1,1), -- 1-based index of item
  30. item_value NVARCHAR(MAX), -- item value
  31. item_rank INT -- item rank if items are not unique
  32. )
  33. AS BEGIN
  34. -- Container for XML
  35. DECLARE
  36. @xml_text NVARCHAR(MAX),
  37. @xml XML;
  38. IF @remove_empty = 1 BEGIN
  39. -- Remove empty inner items
  40. WHILE (CHARINDEX(@delim + @delim, @array, 0) > 0) BEGIN
  41. SET @array = REPLACE(@array, @delim + @delim, @delim);
  42. END;
  43. -- Remove empty left item
  44. IF(LEFT(@array, 1) = @delim) BEGIN
  45. SET @array = SUBSTRING(@array, 2, LEN(@array)-1)
  46. END;
  47. -- Remove empty right item
  48. IF(RIGHT(@array, 1) = @delim) BEGIN
  49. SET @array = SUBSTRING(@array, 1, LEN(@array)-1)
  50. END;
  51. END;
  52. -- Prepare XML-Text
  53. SET @xml_text = N'<L><I>' +
  54. REPLACE(@array, @delim, N'</I><I>') +
  55. N'</I></L>';
  56. -- Convert Array to XML
  57. SET @xml = CAST(@xml_text AS XML);
  58. DECLARE @temp_data TABLE(
  59. item_index INT IDENTITY(1,1),
  60. item_value NVARCHAR(MAX)
  61. )
  62. -- Extract Array Items to Table-Variable
  63. INSERT INTO @temp_data(item_value)
  64. SELECT
  65. item_value = item.value('.', 'NVARCHAR(MAX)')
  66. FROM
  67. @xml.nodes('//I') XMLDATA(item);
  68. -- Calculate Rank for each item (to find non-unique items)
  69. INSERT INTO @data(item_value, item_rank)
  70. SELECT
  71. item_value,
  72. item_rank=RANK() OVER(PARTITION BY item_value ORDER BY item_index, item_value)
  73. FROM
  74. @temp_data
  75. ORDER BY
  76. item_index;
  77. RETURN;
  78. END;
  79. GO

Usages:


  1. -- Get all items
  2. SELECT item_index, item_value = IIF(item_value = '', '---', item_value), item_rank
  3. FROM dbo.tf_array2table(N';ddd;bbb;ccc;;;ccc;ddd;eee;', N';', 0);
Result
item_indexitem_valueitem_rank
1---1
2ddd1
3bbb1
4ccc1
5---2
6---3
7ccc2
8ddd2
9eee1
10---4

  1. -- Get all non-empty items
  2. SELECT * FROM dbo.tf_array2table(N';ddd;bbb;ccc;;;ccc;ddd;eee;', N';', 1);
Result
item_indexitem_valueitem_rank
1ddd1
2bbb1
3ccc1
4ccc2
5ddd2
6eee1

  1. -- Get all non-empty items and filter non-unique
  2. SELECT * FROM dbo.tf_array2table(N';ddd;bbb;ccc;;;ccc;ddd;eee;', N';', 1)
  3. WHERE item_rank=1;
Result
item_indexitem_valueitem_rank
1ddd1
2bbb1
3ccc1
6eee1