Mats codemix

Mats codemix

A little c#, a litte .NET and throw in some c++ and you get a nice Spaghetti

Mats codemix RSS Feed
 
 
 
 

Making groups unique - fixing a duplicate key problem in SQL

I ran into a problem where updating a key column caused duplicate key errors.  The table in question had several columns that formed the Primary Key.  One of those columns was userd for a  serial number making groups of keys unique.

Example

Group

GroupSerial

Apple

1

Apple

1

Pear

1

Pear

1

Here we see that each row is not unique.

So how can we fix this?

First we lay up the data like this, having a unique row number:

Row#

Group

GrpSerial

GrpCnt

GrpMax

1

Apple

1

2

Apple

1

3

Pear

1

4

Pear

1

5

Pear

1

Then we count the items in each group and set that value to each member of the group.

Row#

Group

GrpSerial

GrpCnt

GrpMax

1

Apple

1

2

2

Apple

1

2

3

Pear

1

3

4

Pear

1

3

5

Pear

1

3

Then we the the max row# for each group

Row#

Group

GrpSerial

GrpCnt

GrpMax

1

Apple

1

2

2

2

Apple

1

2

2

3

Pear

1

3

5

4

Pear

1

3

5

5

Pear

1

3

5

Now we can calculate the group serial for each row.

grp_serial = row_serial  - grp_max + grp_cnt

Row#

Group

GrpSerial

GrpCnt

GrpMax

1

Apple

1

2

2

2

Apple

2

2

2

3

Pear

1

3

5

4

Pear

2

3

5

5

Pear

3

3

5

Simple as (apple) pie.

And here is an example in SQL:

 1 /*
 2 How to update a group_serial fixing uniqueness
 3 */
 4 
 5 
 6 -- Create a table with a series of groups 
 7 drop TABLE matstest
 8 go
 9 
10 CREATE TABLE matstest
11 (
12     id numeric identity not null
13     ,row_serial int null
14     -- Value columns
15     ,grp int Null
16     ,grp_serial int null
17     -- helper columns
18     ,grp_cnt int null
19     ,grp_max int null
20 )
21 go
22 
23 -- having a unique index on grp,grp_serial would cause an error 
24 truncate table matstest
25 insert into matstest (grp,grp_serial) values (1,1)
26 insert into matstest (grp,grp_serial) values (22,1)
27 insert into matstest (grp,grp_serial) values (22,1)
28 insert into matstest (grp,grp_serial) values (33,1)
29 insert into matstest (grp,grp_serial) values (33,1)
30 insert into matstest (grp,grp_serial) values (33,1)
31 
32 -- Fake a high row_serial just to prove that low numbers 
33 update matstest set row_serial = id +4311
34 
35 -- Show the starting point
36 select * from matstest
37 
38 --
39 -- It is important to include all the keys that make up the group! In this example there is just one column (grp)
40 -- Find count per group (grp_count)
41 --
42 select grp, count(*) as grp_cnt
43 into #tmp_grp_cnt
44 from matstest
45 group by grp
46 having count(*) > 1 -- Skip this to have count also on the groups with one row
47 
48 update matstest
49 set grp_cnt = ga.grp_cnt
50 from matstest m
51 join #tmp_grp_cnt ga on ga.grp = m.grp
52 
53 --
54 -- Find max row row_serial number per group  (grp_max)
55 --
56 select grp, max(row_serial) as grp_max
57 into #tmp_grp_max
58 from matstest
59 group by grp
60 having count(*) > 1
61 
62 update matstest
63 set grp_max = ga.grp_max
64 from matstest m
65 join #tmp_grp_max ga on ga.grp = m.grp
66 
67 --
68 -- Now we can calculate the serial number per group
69 -- Serial number per group = row_serial - (grp_max) + (grp_cnt)
70 --
71 update matstest set grp_serial = row_serial - (grp_max) + (grp_cnt) where grp_cnt is not null
72 
73 
74 -- show the final result
75 select * from matstest
76 
77 /*
78 
79 -- Starting point
80        id    row        grp grps cnt max
81 1    1    4312    1    1    [NULL]    [NULL]
82 2    2    4313    22    1    [NULL]    [NULL]
83 3    3    4314    22    1    [NULL]    [NULL]
84 4    4    4315    33    1    [NULL]    [NULL]
85 5    5    4316    33    1    [NULL]    [NULL]
86 6    6    4317    33    1    [NULL]    [NULL]
87 
88 
89 -- Final result
90        id    row        grp grps cnt max
91 1    1    4312    1    1    [NULL]    [NULL]
92 2    2    4313    22    1    2    4314
93 3    3    4314    22    2    2    4314
94 4    4    4315    33    1    3    4317
95 5    5    4316    33    2    3    4317
96 6    6    4317    33    3    3    4317
97 */

Generate SQL-code with line breaks from metadata

I’m  updating a system with lots of tables and i wanted to perform the same operation on more than 20 of the tables.  Writing all those update statements by hand is errorprone and using copy-paste is equally worse.

I wanted to write good code with linebreaks, logmessages  and comments. What to do?

Code-generation to the rescue!

 1 -- Find tables with columnname personid
 2 select
 3 ','''+ sysobj.name + '''', syscol.name '.'''+ syscol.name + ''''
 4 
 5 -- Code for updating the column.
 6 ,'
 7 IF EXISTS (SELECT 1 FROM SAMLEDB..'+ sysobj.name + ' WHERE SAMLEDB..'+ sysobj.name + '.'+ syscol.name + ' < 10000)
 8 BEGIN
 9     UPDATE SAMLEDB..'+ sysobj.name + '
10         SET SAMLEDB..'+ sysobj.name + '.'+ syscol.name + ' = (SAMLEDB..'+ sysobj.name + '.'+ syscol.name + ' + 10000)
11         FROM SAMLEDB..'+ sysobj.name + '
12         WHERE SAMLEDB..'+ sysobj.name + '.'+ syscol.name + ' < 10000
13 END
14 ' AS UpdateText
15 from sysobjects sysobj, syscolumns syscol
16 where
17     sysobj.id = syscol.id
18     and syscol.name  like 'personid'
19 order by syscol.name  ,sysobj.name

Which will generate output like:

1 IF EXISTS (SELECT 1 FROM SAMLEDB..PERSON WHERE SAMLEDB..PERSON.personid < 10000)
2 BEGIN
3     UPDATE SAMLEDB..PERSON
4         SET SAMLEDB..PERSON.personid = (SAMLEDB..PERSON.personid + 10000)
5         FROM SAMLEDB..PERSON
6         WHERE SAMLEDB..PERSON.personid < 10000
7 END

Problem solved! I know that the names are correct.  I treat the tables in a uniform way.

SQL - Show tables containing a column

When digging through database tables i use this little select-sctatement to find out which tables that contain a scertain column name.

 1 -- Show tables containing a column
 2 select sysobj.name + '.' + syscol.name
 3 , 'sp_help ' + sysobj.name as sptext
 4 , 'select * from ' + sysobj.name as selall
 5 , 'select count(*) from ' + sysobj.name as selcount
 6 from sysobjects sysobj, syscolumns syscol
 7 where sysobj.id = syscol.id
 8 and syscol.name
 9 like '%somecolumnamer%'
10 and sysobj.name not like 'RPT%'
11 and sysobj.name like '%[_]T'
12 order by syscol.name,sysobj.name

Works on Sybase 12 and probably on MS SqlServer too.

Maybe you have another tip on finding relations between tables using SQL?

Test WLW with plugins

Testing code plugins  for windows Live Writer

Code snippet

   1: using System;

   2: using System.Collections.Generic;

   3: using System.Linq;

   4: using System.Text;

   5:  

   6: namespace blaj.model.entities

   7: {

   8:     /// <summary>

   9:     /// Testar

  10:     /// </summary>

  11:     public class Gubbe

  12:     {

  13:         public string Fornamn { get; set; }

  14:         public int ID { get; set; }

  15:  

  16:     }

  17: }

ASP.NET MVC Gotcha in AccountControllerTest using october theme

I have downloaded ASP:NET MVC RC1 and begun to follow the Contact Manager tutorials.

I downloaded the October theme.  As per the instructuons in this tutorial i copied the theme directories to my MVC-solution.

The theme worked ok but i got a lot of compilation errors in the AccountControllerTest.cs :

Error    17    The type or namespace name 'IMembershipService' could not be found (are you missing a using directive or an assembly reference?)    C:\vsproj\mvc\ContactManager\ContactManager.Tests\Controllers\AccountControllerTest.cs    131    13    ContactManager.Tests
Error    18    The type or namespace name 'AccountMembershipService' could not be found (are you missing a using directive or an assembly reference?)    C:\vsproj\mvc\ContactManager\ContactManager.Tests\Controllers\AccountControllerTest.cs    131    56    ContactManager.Tests
....etc

It turns out that the theme replaces the file AccountController.cs and the version included with the theme seems to be constructed in a different way than the RC1 version.

Solution/Workaround

The october-theme refrences the AccountController actions LogIn and Logout in whereas the RC1 actions are called LogOn and LogOff respectively.

This can be changed in the following files:

Replace the the AccountController.cs with the one that came with MVC RC1 and make the following modifications.

// Add this function
public ActionResult LoginPartial()
{
return View();
}

The above method gets called here : Views\Shared\Site.Master(48)

initLogin('<%= Url.Action("LoginPartial", "Account") %>','<%= Url.Action("Login", "Account") %>');

Make changes to LoginLinkHelper.cs


if (isAuthenticated)
{
sb.Append(helper.ActionLink("Logout", "LogOff", "Account"));
}
else
{
sb.Append(helper.ActionLink("Login", "LogOn", "Account"));
}

Make changes to \Views\Account\LoginPartial.aspx(6)

[sourcecode language='ASP']

<% using (Html.BeginForm(”LogOn”, “Account”)) { %>

[/sourcecode]

So far i havent noticed any different behaviour in the logon/logoff behaviour.

The best solution would of course be to make these changes to the files in the October theme itself so it will be vompatible with the RC1 and in the future the v 1.0 of MVC.

Searching with google

If i want to search this site with google.


Code

<form action="http://www.google.com/search" method="get">  
<fieldset>  
<input type="hidden" name="sitesearch" value="mats.gardstad.se/matscodemix/" />  
<input type="text" name="q" size="31" maxlength="255" value="" />  
<input type="submit" value="Google Search" />  
</fieldset>  
</form>

Calling 7-Zip from powershell

I had to write a little utility-script to backup and restore some files used for testdata. First i tried a commandline plugin for IZarc which seemed to select the wrong set of files…

I decided to use 7-Zip after reading this post:John Robbins blog which helped me with the -o option.

What i struggled with was how to call 7-Zip, but The PowerShell Guy came to my rescue.

So here is my little example script to call 7-Zip from powershell.

# Alias for 7-zip
if (-not (test-path "$env:ProgramFiles\7-Zip\7z.exe")) {throw "$env:ProgramFiles\7-Zip\7z.exe needed"}
set-alias sz "$env:ProgramFiles\7-Zip\7z.exe"
 
# directories to use
$base = 'C:\dev\ps\ziptest\'
$Extract = 'C:\dev\ps\ziptest\to dir\'
$zipdir = 'C:\dev\ps\ziptest\zip dir\'
 
# Zip-file name
$yy = "test"
$zipfile = $zipdir + 'zz' + $yy+ '.zip'
$zipoption = ' -tzip "' + $zipfile + '"'
 
# Files to compress
$from = $base + "from\*.*"
 
# Create zip-file
write-host "from:" $from
write-host "zipopt:" $zipoption
 
remove-item "$zipfile"
sz a -tzip "$zipfile" $from
 
# Extract files from archive
 
# Since the -o option to 7Z.EXE cannot have a space between it and the
# directory there's a bit of a problem. PowerShell does not expand the
# line -o$Extract if passed directly on the command line.
# John Robbins blog
# http://www.wintellect.com/cs/blogs/jrobbins/archive/2008/04/20/easily-downloading-and-installing-the-sysinternals-suite.aspx
$outputOption = "-o$Extract"
Write-Output "Extracting files into $Extract"
write-host "output:" $outputOption
sz e -tzip "$zipfile" $outputOption -y
 
# Show contents of archive
sz l -tzip "$zipfile"

Powershell v2 CTP3 - profiles

I wanted to change the colours for powersshell error messages. Easily done by using this code

# Set Error and warning colours
$hostprivate = (Get-Host).PrivateData
$hostprivate.ErrorBackgroundColor    = "red"

First i put the code in Profile.ps1 in C:\Documents and Settings\myname\Mina dokument\WindowsPowerShell.
This caused the powerhell scripting environment (ISE) to spit out some errors while starting:

Property ‘ErrorBackgroundColor’ cannot be found on this object; make sure it exists and is settable.

At C:\Documents and Settings\maga\Mina dokument\WindowsPowerShell\profile.ps1:201 char:14

+ $hostprivate. <<<< ErrorBackgroundColor    = “magenta”
+ CategoryInfo          :
InvalidOperation: (ErrorBackgroundColor:String) [], RuntimeException

+ FullyQualifiedErrorId : PropertyNotFound

To solve it, put the codelines in Microsoft.PowerShell_profile.ps1 instead.

Testing geshi

Use Geshi (wp-syntax)

Below should be nice looking colour coded pwoershell code

# Remember to write  pre lang="powershell" NOT posh or something other cryptic....
Add-Content powershell
Add-History

End of Geshi example

SSIS - Deployment utility problem - cannot copy configfile

In a SSIS-project you can set the  property “Create deployment utility” to true. When building such a project you may get the error

“Could not copy file xxx.dtsconfig to the deployment utility output directory. File already exists.”

This was due to two packages using the same config file and Visual Studio wants to copy that file twice and errs on the second attempt.

WORKAROUND:

  1. Manually create a deployment-manifest. Name the file  “ProjectName.SSISDeploymentManifest”. Save the file to the folder Drive:\FullPathToTheSSISProject\Install.
  2. Copy the needed files from the bin-folder to the install-folder

Example manifest-file:


<?xml version="1.0" ?>
<DTSDeploymentManifest GeneratedBy="<Domain>\<UserName>" GeneratedFromProjectName="<ProjectName>"
GeneratedDate="<Date><Time>" AllowConfigurationChanges="true">
<Package>Package1.dtsx</Package>
<Package>Package.dtsx</Package>
<ConfigurationFile>cp.xml</ConfigurationFile>
<ConfigurationFile>cc.xml</ConfigurationFile>
</DTSDeploymentManifest>

See also: http://support.microsoft.com/kb/910419