DECLARE @v_name NVARCHAR(MAX)
DECLARE v_cursor CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY FOR
select * from ....
OPEN v_cursor
WHILE 1=1
BEGIN
FETCH NEXT FROM v_cursor INTO @v_name
IF @@fetch_status = 0
BEGIN
Begin TRY
.....
End TRY
Begin CATCH
PRINT Error_Message()
BREAK
End CATCH
END
ELSE BREAK
End
CLOSE v_cursor
DEALLOCATE v_cursor
W SSMS przydałby się edytor snippetów. Może kiedyś będzie Resharper dla t-SQL'a :)
Pokazywanie postów oznaczonych etykietą code. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą code. Pokaż wszystkie posty
czwartek, 3 marca 2016
Szablon do sql kursora
Pewne rzeczy nie można przeskoczyć - takie jak kursor w sql server. Często szukam przykładów z użyciem kursora, więc postanowiłem zamieścić szablon.
piątek, 29 stycznia 2016
Liczenie w SQL Server za pomocą CTE i ROW_NUMBER
Czasami proste rzeczy są lepsze, ale żeby te proste rzeczy znaleźć to potrzeba czasu.
Aby policzyć od 1 do 10000 to będziemy mogli wykorzystać pseudorekurencja, która opisałem wcześniej na blogu.
Aby policzyć od 1 do 10000 to będziemy mogli wykorzystać pseudorekurencja, która opisałem wcześniej na blogu.
with cte (row) as ( SELECT 1 UNION all SELECT c.row+1 FROM cte c where c.row < 10000 ) select * from cte OPTION (MAXRECURSION 10000)Ale wykorzystanie funkcji ROW_NUMBER jest dużo lepszym sposobem:
SELECT TOP (10000) ROW_NUMBER() OVER (ORDER BY s1.[object_id]) as row FROM sys.all_objects AS s1 CROSS JOIN sys.all_objects AS s2
środa, 16 grudnia 2015
Dekompozycja projektu w R
Robiłem prezentacje i zastanawiałem się jak można zapisać cały projekt. Szukałem po sieci jakiś dobrych praktyk do dekompozycji plików. Ja wykombinowałem podział całego projektu na kilka plików:
Co w tych plikach jest? Zaczynając od początku:
Install.r - instalacja pakietów (ten skrypt wykonujemy tylko raz, aby pobrać paczki)
Functions.r - wszystkie funkcje
Load.r - ładowanie pakietów oraz przygotowywanie danych do prezentacji
Presentation.rpres - warstwa prezentacji, równie dobrze możne być to plik w Rmd czy z użyciem shiny (powód istnienia tego pliku jest TYLKO wyświetlanie tych danych jakie chcemy przedstawić, nie ma logiki biznesowej).
custom.css - customizacja warstwy prezentacji, czyli overridowane i dodatkowe css'y
I na końcu w folderze figures mam wszystkie zdjęcia.
Prezentacja o najpopularniejszych kryzysach ekonomicznych nie jest skończona. Jeszcze dużo chciałoby się dodać. Zaletą rpres jest to, że za każdym razem możemy mieć aktualne dane na prezentacji.
Poniżej rezultat wygenerowanej prezentacji:
Co w tych plikach jest? Zaczynając od początku:
Install.r - instalacja pakietów (ten skrypt wykonujemy tylko raz, aby pobrać paczki)
Functions.r - wszystkie funkcje
Load.r - ładowanie pakietów oraz przygotowywanie danych do prezentacji
Presentation.rpres - warstwa prezentacji, równie dobrze możne być to plik w Rmd czy z użyciem shiny (powód istnienia tego pliku jest TYLKO wyświetlanie tych danych jakie chcemy przedstawić, nie ma logiki biznesowej).
custom.css - customizacja warstwy prezentacji, czyli overridowane i dodatkowe css'y
I na końcu w folderze figures mam wszystkie zdjęcia.
Prezentacja o najpopularniejszych kryzysach ekonomicznych nie jest skończona. Jeszcze dużo chciałoby się dodać. Zaletą rpres jest to, że za każdym razem możemy mieć aktualne dane na prezentacji.
Poniżej rezultat wygenerowanej prezentacji:
środa, 7 października 2015
Zmiana culture w bloku using
Bawiłem się kultura i potrzebuję kodu, który zostanie wywołany dla konkretnej kultury, a później wróci do poprzedniej kultury. Żeby to zrobić najprościej to należy użyć interfejsu IDisposable i tyle.
public class CultureContext : IDisposable
{
private CultureInfo contextCultureInfo = null;
private CultureInfo exitCultureInfo = null;
public CultureContext(CultureInfo ci)
{
exitCultureInfo = GetCulture();
contextCultureInfo = ci;
SetCulture(ci);
}
public void Dispose()
{
SetCulture(exitCultureInfo);
}
public virtual void SetCulture(CultureInfo ci)
{
Thread.CurrentThread.CurrentUICulture = ci;
Thread.CurrentThread.CurrentCulture = ci;
}
public virtual CultureInfo GetCulture()
{
return Thread.CurrentThread.CurrentCulture;
}
}
Wtedy zastosowanie kultury w bloku using jest proste:using (new CultureContext(new CultureInfo("en-US")))
{
...
}
Im prostsze rozwiązanie tym lepsze :)poniedziałek, 5 października 2015
Walidacja bibliotek jako krok w CI
Skąd wież, że masz poprawne informacje w biblioteczce (.dll)?
Skąd wież, że wszystkie biblioteki w projekcie mają taką samą wersję lub nazwę produktu?
Ja napisałem mały skrypcik do walidacji biblioteczek pod względem wersji. Poniżej jest skrypt:
Skąd wież, że wszystkie biblioteki w projekcie mają taką samą wersję lub nazwę produktu?
Ja napisałem mały skrypcik do walidacji biblioteczek pod względem wersji. Poniżej jest skrypt:
function Test-AssemblyVersion
{
param(
[Parameter(
Position=0,
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)
]
[String]
$filePath
,
[Parameter(
ValueFromPipeline=$false)
]
[String]
$ProductVersion
,
[Parameter(
ValueFromPipeline=$false)
]
[String]
$FileVersion
)
begin{
$invIC= [System.StringComparison]::InvariantCultureIgnoreCase
}
process {
$file = $_
if(test-path $file)
{
$ass = [System.Reflection.Assembly]::LoadFile($file)
$fvi = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($ass.Location)
if( $ProductVersion.Equals($fvi.ProductVersion, $invIC)
-and $FileVersion.Equals($fvi.FileVersion, $invIC) )
{
Write-Host "$file is ok."
}
else
{
throw "Wrong ProductVersion or FileVersion in $file."
}
}
}
}
Przykładowe wywołanie przedstawione jest poniżej:gci -path .\MyProject\bin\ -recurse -filter 'MyProject.*.dll' | Test-AssemblyVersion -ProductVersion 2.1.0.0 -FileVersion 2.1.0.0Można teraz dodać wywołanie i funkcję do jednego pliku i cały kod wyglądałby tak:
param(
[string]$ProductVersion,
[string]$FileVersion
)
function Test-AssemblyVersion
{
param(
[Parameter(
Position=0,
Mandatory=$true,
ValueFromPipeline=$true,
ValueFromPipelineByPropertyName=$true)
]
[ValidateNotNullOrEmpty()]
[String]
$filePath
,
[Parameter(
Position=1,
Mandatory=$true,
ValueFromPipeline=$false,
ValueFromPipelineByPropertyName=$false)
]
[ValidateNotNullOrEmpty()]
[String]
$ProductVersion
,
[Parameter(
Position=2,
Mandatory=$true,
ValueFromPipeline=$false,
ValueFromPipelineByPropertyName=$false)
]
[ValidateNotNullOrEmpty()]
[String]
$FileVersion
)
begin{
$invIC= [System.StringComparison]::InvariantCultureIgnoreCase
}
process {
$file = $_
if(test-path $file)
{
$ass = [System.Reflection.Assembly]::LoadFile($file)
$fvi = [System.Diagnostics.FileVersionInfo]::GetVersionInfo($ass.Location)
if( $ProductVersion.Equals($fvi.ProductVersion, $invIC) -and $FileVersion.Equals($fvi.FileVersion, $invIC) )
{
Write-Host "$File is ok. ProductVersion is $($fvi.ProductVersion) and FileVersion is $($fvi.FileVersion)"
}
else
{
throw "Wrong ProductVersion or FileVersion in $file. ProductVersion is $($fvi.ProductVersion) and FileVersion is $($fvi.FileVersion)"
}
}
}
}
$root = ( gi '..\..' ).FullName
Write-host "Root is $root"
$OutputPackage = "$root\bin"
if(Test-path $OutputPackage)
{
$fcraLib = gci -path $OutputPackager -filter 'MyProject.*.dll'
if( $fcraLib)
{
$fcraLib | %{$_.Fullname} | Test-AssemblyVersion -ProductVersion $ProductVersion -FileVersion $FileVersion
}
else
{
throw "No dll files in dir: $OutputPackage "
}
}
Teraz można ten skrypt podpiąć do TeamCity.środa, 30 września 2015
Culture w PS
Ostatnio w projekcie jakieś cyrki pojawiały się. Postanowiłem troszkę porozglądać się po kulturze (formatowaniu dat) i napisałem skrypcik do wyświetlania daty dla wszystkich kultur.
Na początku potrzebujemy funkcję zwracającą listę kultur:
Przykłady jak będzie wyglądał LongDate, LongTime i ShortDate, ShortTime można sprawdzić pod tym linkiem
Na początku potrzebujemy funkcję zwracającą listę kultur:
function Get-Culture
{
[System.Globalization.CultureInfo]::GetCultures([System.Globalization.CultureTypes]::AllCultures) | %{
try{
[System.Globalization.CultureInfo]::CreateSpecificCulture($_.Name)
}catch {}
}
}
Wywołąnie jest następujące:$dt = Get-Date
Get-Culture
| %{ New-Object PSObject -Property @{Str = $dt.ToString($_); Name = $_.Name; DisplayName = $_.DisplayName} }
| sort -Property Name
Teraz to można wyeksportować do pliku taki wynik. Poniżej zdjęcie jak wygląda rezultat wywołania w konsoli:Przykłady jak będzie wyglądał LongDate, LongTime i ShortDate, ShortTime można sprawdzić pod tym linkiem
sobota, 15 sierpnia 2015
Exportowanie praw dostępu w PS
Krótki kodzik, ale przydatny przy sprawdzaniu praw dostępu do plików. Chcesz wiedzieć jakie są uprawnienia - wystarczy, że uruchomisz ten PS skrypt, a on wygeneruje csv z uprawnieniami.
function Export-IOPermissions
{
[cmdletbinding()]
param(
[ValidateScript({Test-Path $_ -PathType 'Container'})]
[string]
$dir='.'
,
[ValidateNotNullOrEmpty()]
[string]
$outputcsv = '..\permissions.csv'
,
[ValidateNotNullOrEmpty()]
[string]
$Delimiter = ','
)
$prop = @('Path', 'Group', 'Owner', 'Sddl', 'PSChildName', 'AccessRightType', 'AccessRuleType', 'AreAccessRulesCanonical', 'AreAccessRulesProtected', 'AreAuditRulesCanonical', 'AreAuditRulesProtected', 'AuditRuleType', 'AccessToString', 'AuditToString')
if( Test-path $outputcsv)
{
Remove-Item -path $outputcsv | out-null
}
gci -Recurse -path $dir | Get-Acl | select -Property $prop -Expand Access | Sort-Object -Property, Path, FileSystemRights, IdentityReference | Export-Csv -Path $outputcsv -Delimiter $Delimiter -NoTypeInformation -Force
}
środa, 5 sierpnia 2015
Tworzenie backup'u za pomocą scripter'a w PS
Ostatnio musiałem tworzyć skrypty do tworzenia bazy danych (od zera) w sql server. Kod zamieszczony jest na github'ie i projekt nazywa się BackupSqlServerPS. Projekt nazwałem backupem bo oprócz generowania t-sql skryptów do bazy danych to są tworzone skrypty do sql server jobów, zapisywane są logi z bazy i jobów, zapisywane są dane z tabel w formacie csv oraz tworzone są skrypty z danymi w zapytaniami "insert into values". Projekt jest w budowie. Jeszcze chciałbym dodać export użytkowników, execution planów, diagramów i assemblies oraz tworzenie bak'ów. Jeszcze dużo przede mną :)
Najważniejszą funkcją do backupów jest tworzenie scryptera. Z mechanizmu skryptera pewnie już korzystałeś
Za każdym razem jak w Sql Server Management Studio przez menu kontekstowe tworzysz zapytanie 'select ...', create, drop lub podobne to, to zapytanie jest tworzone przez scryptera.
Tak na prawdę skrypter to obiekt klasy Microsoft.SqlServer.Management.SMO.Scripter z pakietu Sqlserver Management Objects. Najwięcej problemów było przy ustawianiu opcji scriptera. Poniżej zamieściłem, krótki przykład tworzenia skryptów do tworzenia tabel w bazie danych. Oczywiście napisałem to w PS.
Na początku musisz załadować SMO:
Tworzymy obiekt reprezentujący server:
Tworzymy obiekt grający pierwsze skrzypce:
Teraz wyciągamy wszystkie obiekty z bazy danych
Skrypt główny, który robi wszystko ('śpiewa i tańczy') nazywa się backup-sqlserver.ps1
Oprócz tabel to tworzymy skrypty do Stored Procedures, widoków, user-defined functions oraz typów.
Po uruchomieniu skryptu będziemy mieli foldery w którym będą skrypty do tworzenia obiektów.
Najważniejszą funkcją do backupów jest tworzenie scryptera. Z mechanizmu skryptera pewnie już korzystałeś
Za każdym razem jak w Sql Server Management Studio przez menu kontekstowe tworzysz zapytanie 'select ...', create, drop lub podobne to, to zapytanie jest tworzone przez scryptera.
Tak na prawdę skrypter to obiekt klasy Microsoft.SqlServer.Management.SMO.Scripter z pakietu Sqlserver Management Objects. Najwięcej problemów było przy ustawianiu opcji scriptera. Poniżej zamieściłem, krótki przykład tworzenia skryptów do tworzenia tabel w bazie danych. Oczywiście napisałem to w PS.
Na początku musisz załadować SMO:
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.ConnectionInfo")
[System.Reflection.Assembly]::LoadWithPartialName("Microsoft.SqlServer.SMO")
Tworzymy obiekt reprezentujący server:
$srvConn = new-object Microsoft.SqlServer.Management.Common.ServerConnection $srvConn.ServerInstance = 'localhost' $srvConn.Login = 'user' $srvConn.Password = 'passwd' $srv = New-Object Microsoft.SqlServer.Management.SMO.Server($srvConn)
Tworzymy obiekt grający pierwsze skrzypce:
$scripter = New-Object Microsoft.SqlServer.Management.SMO.Scripter($srv) $scripter.Options.IncludeDatabaseContext = $false $scripter.Options.WithDependencies = $false $scripter.Options.AllowSystemObjects = $false $scripter.Options.NoCommandTerminator = $false $scripter.Options.ScriptSchema = $true $scripter.Options.SchemaQualify = $true $scripter.Options.DriAll = $true
Teraz wyciągamy wszystkie obiekty z bazy danych
$db = $srv.Databases['database']
$tbl = $db.tables | ? {-not $_.IsSystemObject }
foreach ($o in $tbl) {
{
$scripter.Options.FileName = $o.Name + ".sql"
$scripter.Script($o)
}
Jeżeli będziemy tworzyć inserty z danymi to zamiast $scripter.Script($o) powinniśmy wywołać $scripter.EnumScript($o)Skrypt główny, który robi wszystko ('śpiewa i tańczy') nazywa się backup-sqlserver.ps1
Oprócz tabel to tworzymy skrypty do Stored Procedures, widoków, user-defined functions oraz typów.
Po uruchomieniu skryptu będziemy mieli foldery w którym będą skrypty do tworzenia obiektów.
sobota, 1 sierpnia 2015
Pseudorekurencja w SqlServer
Już dawno nie miałem styczności z SqlServerem więc muszę sobie przypomnieć. Dla treningu zrobiłem zapytanie rekurencyjne (pseudorekurencje). Już taki skrypt wykonałem dla Oracla i muszę powiedzieć, że kod wygląda identycznie :)
Dla problemu liczby fibonacciego tworzymy CTE:
Arithmetic overflow error for type int, value = 2147483648.000000.
Możemy zmienić typ kolumny w zapytaniu. Do sprawdzenia typu pomocne nam będzie funkcja SQL_VARIANT_PROPERTY. Przykład wykorzystania takiej funkcji jest poniżej:
Dla problemu liczby fibonacciego tworzymy CTE:
WITH Fibonacci_cte AS
(
SELECT 0 as PrevFibN, 1 as FibN, 1 as Nr
UNION ALL
SELECT FibN, PrevFibN + FibN, Nr+1
FROM Fibonacci_cte
WHERE FibN < 1000000000
)
select * from Fibonacci_cte
Wynik zapytania wygląda tak:
Dla problemu wieży Hanoi mamy model:
with Hanoi_cte AS ( select 1 as n, 1 as counts union all select n +1 as n, POWER(2, n+1) -1 as counts from Hanoi_cte where n < 30 ) select * from Hanoi_cteWyniki zapytania są poniżej: W tych przykładach dostaje często wyjątek związany z przekroczeniem dozwolonej wielkości dla typu kolumny (int).
Arithmetic overflow error for type int, value = 2147483648.000000.
Możemy zmienić typ kolumny w zapytaniu. Do sprawdzenia typu pomocne nam będzie funkcja SQL_VARIANT_PROPERTY. Przykład wykorzystania takiej funkcji jest poniżej:
select SQL_VARIANT_PROPERTY(POWER(CAST (2 as bigint), 2) , 'BaseType')Kiedy dodamy kastowanie do bigint to możemy już uzyskiwać większe liczby.
with Hanoi_cte AS ( select CAST (1 AS bigint) as n, CAST (1 AS bigint) as counts union all select n + 1 as n ,POWER(CAST (2 as bigint), n+1) -1 as counts from Hanoi_cte where counts < CAST(0x3FFFFFFFFFFFFFFF AS bigint) ) select * from Hanoi_cteAle i tak dla tego problemu możemy iterować do 63 rekordu - czy wiesz może dlaczego :) Tak samo dodałem kastowanie do bigint dla problemu fibonacciego:
WITH Fibonacci_cte AS
(
SELECT
CAST (0 as bigint) as PrevFibN,
CAST(1 as bigint) as FibN,
1 as Nr
UNION ALL
SELECT
FibN,
PrevFibN +FibN,
Nr+1
FROM Fibonacci_cte
WHERE Nr < 92
)
select * from Fibonacci_cte
A wyniki tego zapytania są poniżej:
piątek, 29 maja 2015
Rozdzielanie argumentów cmd w PS
Jak pracowałeś z aplikacjami konsolowymi z gigantyczną liczba argumentów to za pewne dostawałeś oczopląsu - ja tak miałem. Mam mały skrypcik do rozwiązania tego problemu:
function Split-Args
{
param(
[ValidatenotNullOrEmpty()]
[string]$argStr
)
[hashtable]$arg =@{}
[regex]::Matches($argStr, '-.+') | %{
[string]$value = $_.Groups[0].Value
[string]$first, $second = $value.Split([char[]]@(' ','=',':'),2)
$first = $first.Replace('-','')
$arg[$first] = $second
}
return $arg.GetEnumerator() | sort Name
}
Wywołanie jest następujące:Split-Args "-unzip=true -zippedFile zipfile1.ZIP -unzipFolder .\unzips.temp\ --date sysdate -year:2014 "Przyjemniej szuka się parametrów posortowanych po kluczu w kilku linijkach niż w jednej lini:
sobota, 9 maja 2015
For loop dla msbuild
Jak będziesz chciał uruchomić kilka razy dany proces to możesz wykorzystać polecenia for. Ja tak muszę z budowaniem projektów w .NETcie. Musze 2 razy uruchomić msbuild'a. Niektóre projekty nie maja referencji do innych projektów w solucji, ale do zbudowanych bibliotek w specjalnym folderze. Problem też może być jak jedno budowanie projektu potrzebuje dużo czasu (dajmy na to 25 minut) i w każdym momencie może wystąpić błąd.
Polecenie do dwukrotnego budowania jest poniżej:
Takie proste, a tak upraszcza życie :)
Polecenie do dwukrotnego budowania jest poniżej:
for /L %n in (1,1,2) do ( msbuild myProject.sln /m )
Takie proste, a tak upraszcza życie :)
wtorek, 21 kwietnia 2015
Brakujące dni w Oraclu
Któryś raz piszę to samo zapytanie, więc postanowiłem zapisać to rozwiązanie na blogu. Może Ci się przyda - mi na pewno :)
Przypuśćmy, że masz tabelę (o nazwie tableWithDate), która ma kolumnę z datą (dateToCheck). Ładujesz dane i zapisujesz dzień na które dane zostały załadowane. Na końcu ładowania danych powinieneś mieć wypełnioną tabelę z dla każdego dnia od 1 stycznia 2014. Chciałbyś sprawdzić dla jakich dni nie zostały załadowane dane.
Pomocne nam będą funkcje do operowania na dacie. Same zapytanie wygląda następująco:
Przypuśćmy, że masz tabelę (o nazwie tableWithDate), która ma kolumnę z datą (dateToCheck). Ładujesz dane i zapisujesz dzień na które dane zostały załadowane. Na końcu ładowania danych powinieneś mieć wypełnioną tabelę z dla każdego dnia od 1 stycznia 2014. Chciałbyś sprawdzić dla jakich dni nie zostały załadowane dane.
Pomocne nam będą funkcje do operowania na dacie. Same zapytanie wygląda następująco:
WITH dates as (
SELECT TRUNC(to_date('1/1/2014','DD/MM/YYYY')) -1 + LEVEL AS d
FROM DUAL
CONNECT BY LEVEL <= 365
)
SELECT
d As "Date",
to_char(d, 'DDD') As "DayOfYear",
to_char(d, 'Day') as "DayOfWeek",
to_char(d, 'WW') As "WeekOfYear",
to_char(d, 'Month') As "MonthName",
to_char(d, 'RM') As "Roman" ,
to_char(d, 'Q') As "Quarter"
FROM dates d
where NOT EXISTS (
SELECT tab.dateToCheck
from tableWithDate tab
where to_char(d.d, 'YYYYMMDD') = to_char(tab.dateToCheck, 'YYYYMMDD')
group by tab.dateToCheck
)
ORDER BY d
;
Do wyświetlenia daty pomocna nam będzie funkcja to_char z formatowaniem.
Jeżeli nie będziemy mieli załadowanych danych to wynik zapytania będzie wyglądał następująco:
Jeżeli załadujemy dane i w śród tych danych nie będzie danych dla dni: 1-3 i 6 listopada, to tak będzie wyglądał wynik zapytania:
czwartek, 2 października 2014
Jednolinijkowce do PATH w PS
Coraz częściej zamiast dużych projektów w PS piszę jednolinijkowce.
Jednym z takich przydatnych jednolinijkowców jest skrypt pokazujący ścieżkę do programu, który znajduje się w PATH:
Dodatkowo możemy stworzyć funkcje dla wywołania oraz sprawdzanie czy istnieje ścieżka do katalogu:
Czasami jak uruchamiam sql plusa to nie wiem jaka jest ścieżka do programu, więc uruchamiam jednolinijkowca:
Wynik dostaje następujący:
Bonus:
Z pomocą może Ci przyjść również komenda where, która już wyświetlania ścieżki do programu
Jak chcesz wiedzieć, gdzie jest ten program to wpisz:
Powstało troszkę mitów o tym.
Jednym z takich przydatnych jednolinijkowców jest skrypt pokazujący ścieżkę do programu, który znajduje się w PATH:
$env:path.Split(';') | gci -Filter 'app.exe'
Dodatkowo możemy stworzyć funkcje dla wywołania oraz sprawdzanie czy istnieje ścieżka do katalogu:
function Find-EnvPath
{
param(
[string]$Filter = "*"
)
return $env:path.Split(';')
| select -Unique
| ? {$_ -and (test-path $_)}
| gci -Filter $Filter
}
Czasami jak uruchamiam sql plusa to nie wiem jaka jest ścieżka do programu, więc uruchamiam jednolinijkowca:
Find-EnvPath "sqlplus.exe" | select FullName
Wynik dostaje następujący:
Bonus:
Z pomocą może Ci przyjść również komenda where, która już wyświetlania ścieżki do programu
where sqlplus.exe
Jak chcesz wiedzieć, gdzie jest ten program to wpisz:
where whereTo tak samo gdybyś wpisał w wyszukiwarce google słowo 'google' :)
Powstało troszkę mitów o tym.
niedziela, 31 sierpnia 2014
UnGroup w PS
Ungroup to alias, jakiego mi brakowało w PowerShellu. Bardzo często grupuje obiekty, ale nie ma bezpośredniej możliwości odgrupowania tej kolekcji. Poniższy kod przedstawia tą możliwość:
Stwórzmy alias do funkcji ungroup-object:
Przykładem takiego odgrupowania może być wyświetlenie wszystkich definicji aliasów, które maja więcej niż jeden alias:
function ungroup-object
{
process {
$_ | %{$_.Group | %{$_} }
}
}
Odgrupowanie wygląda jak by brało udział w konkursie na najbardziej nieczytelny kod w PS:)Stwórzmy alias do funkcji ungroup-object:
New-Alias -Name 'ungroup' -Value 'ungroup-object' -Description 'Ungroup object which where grouped'
Przykładem takiego odgrupowania może być wyświetlenie wszystkich definicji aliasów, które maja więcej niż jeden alias:
Get-Alias | group definition | ? {$_.Count -gt 1} | ungroup | sort definition | select name, definition | Out-GridView
Okazało się że takich aliasów mam z 53. Poniżej zamieszczona jest część wyników: Help dla oracla w PS
Co najbardziej mnie denerwuje w oraclu to ilość błędów jakie dostaje ze względu na infrastrukturę lub złożone ustawienia oracla. Natomiast te błędy są całkiem dobrze udokumentowane m.in dzięki portalowi ora-code. Napisałem skrypcik do pobierania informacji o błędach z takiej strony. Skrypt znajduje się w module PS do Oracle, przy którym ostatnio rozwijam.
Skrypt jest podany poniżej:
Oprócz całego kodu błedu to można wpisać tylko numer błędu:
Skrypt jest podany poniżej:
function Get-OracleHelp
{
param(
[ValidateNotNullOrEmpty()]
[Alias('code')]
[string]$errorCode
)
[ref]$errorNumber=0
$isNumber = [int]::TryParse($errorCode, $errorNumber)
if($isNumber)
{
$errorCode = 'ORA-{0:00000}' -f $errorNumber.Value
}
$errorCode = $errorCode.ToUpper()
$url = "http://{0}.ora-code.com/" -f $errorCode
$request = Invoke-WebRequest -Uri $url -UseDefaultCredentials
$table = $request.ParsedHtml.body.getElementsByTagName('table')
$trs = $table | %{$_.getElementsByTagName('tr')} | ? {$_.getAttributeNode('valign').value -eq 'top' }
$desc=''
$cause=''
$action=''
$trs | %{
[string]$text = $_.innerText
if( $text.StartsWith($errorCode, 'CurrentCultureIgnoreCase'))
{
$desc = $text.Remove(0, $errorCode.Length+1)
}
if( $text.StartsWith('Cause', 'CurrentCultureIgnoreCase'))
{
$cause = $text.Remove(0, 'Cause'.Length+1)
}
if( $text.StartsWith('Action', 'CurrentCultureIgnoreCase'))
{
$action = $text.Remove(0, 'Action'.Length+1)
}
}
return new-object PSObject `
| Add-Member -MemberType NoteProperty -PassThru -Name 'Code' -Value $errorCode `
| Add-Member -MemberType NoteProperty -PassThru -Name 'Description' -Value $desc `
| Add-Member -MemberType NoteProperty -PassThru -Name 'Cause' -Value $cause `
| Add-Member -MemberType NoteProperty -PassThru -Name 'Action' -Value $action `
| Add-Member -MemberType NoteProperty -PassThru -Name 'Url' -Value $url
}
Wywołanie wygląda następująco:Get-OracleHelp -errorCode ORA-01243I dostajemy taki opis błędu:
Oprócz całego kodu błedu to można wpisać tylko numer błędu:
Get-OracleHelp -code 12154
sobota, 30 sierpnia 2014
Dynamiczne parametry funkcji w PS ( dynamicParam )
ValidateSet jest fantastycznym atrybutem do parametrów funkcji. Umożliwia podpowiadanie argumentów oraz nie dopuszcza zmienianie wartości. Ma jednak jedną wadę. dozwolone wartości musza być stałymi przedefiniowanymi. Jeżeli chcielibyśmy zwalidować dane wejściowe, które ilość oraz wartości są zmienne to nie mamy takiej możliwości. Z pomocą przychodzi nam dynamiczny parametr. Całkowicie zmienia składnie funkcji. Przykład wywołania dynamicznego parametru jest poniżej. Przypuśćmy, że mamy hashtable z dozwolonymi nazwami oraz ich wartościami
Można stworzyć osobną funkcję, która będzie zwracała RuntimeDefinedParameterDictionary z dynamicznymi parametrami:
Dynamiczny parametr pierwszy raz zastosowałem jak pisałem moduł do Oracla. W jednej zmiennej przetrzymuje zapytania sql, a funkcja z parametrem dynamicznym wywołuje to zapytanie sql:
Cały kod modułu jest na githubie.
$dparamColl = @{
param1 = 'test';
param2 = 'abc';
param3 = 'xyz'
}
function function_name
{
param(
$param
)
dynamicParam {
$attributes = new-object System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = "__AllParameterSets"
$attributes.Mandatory = $true
$attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attributes)
$_Values = $dparamColl.Keys
$ValidateSet = new-object System.Management.Automation.ValidateSetAttribute($_Values)
$attributeCollection.Add($ValidateSet)
$dynParam1 = new-object -Type System.Management.Automation.RuntimeDefinedParameter( "dparam", [string], $attributeCollection)
$paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add("dparam", $dynParam1)
return $paramDictionary
}
begin{}
process{
return $dparam
}
end{}
}
Można stworzyć osobną funkcję, która będzie zwracała RuntimeDefinedParameterDictionary z dynamicznymi parametrami:
function Get-DynamicParam
{
param(
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[array]$paramSet,
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]$paramName)
$attributes = new-object System.Management.Automation.ParameterAttribute
$attributes.ParameterSetName = "__AllParameterSets"
$attributes.Mandatory = $true
$attributeCollection = new-object -Type System.Collections.ObjectModel.Collection[System.Attribute]
$attributeCollection.Add($attributes)
$_Values = $paramSet
$ValidateSet = new-object System.Management.Automation.ValidateSetAttribute($_Values)
$attributeCollection.Add($ValidateSet)
$dynParam1 = new-object -Type System.Management.Automation.RuntimeDefinedParameter( $paramName, [string], $attributeCollection)
$paramDictionary = new-object -Type System.Management.Automation.RuntimeDefinedParameterDictionary
$paramDictionary.Add($paramName, $dynParam1)
return $paramDictionary
}
Dynamiczny parametr pierwszy raz zastosowałem jak pisałem moduł do Oracla. W jednej zmiennej przetrzymuje zapytania sql, a funkcja z parametrem dynamicznym wywołuje to zapytanie sql:
$sqlHelpQueryCollection = @{
DatabaseName = @'
select ora_database_name from dual
'@;
Instance = @'
select * from v$instance
'@;
#and many more
}
function Invoke-OracleSqlHelpQuery
{
[CmdletBinding()]
param(
$conn
)
dynamicParam {
return Get-DynamicParam -paramSet $sqlHelpQueryCollection.Keys -paramName 'sqlHelpQuery'
}
begin{}
process{
$sql = $sqlHelpQueryCollection[$sqlHelpQuery]
return Get-OracleDataTable -conn $conn -sql $sql
}
end{}
}
Efekt dynamicznego parametru jest przedstawiony poniżej:Cały kod modułu jest na githubie.
niedziela, 24 sierpnia 2014
PowerShell moduł do Oracle
Zacząłem pisać ps moduł do bazy danych w Oraclu. Kod źródłowy jest na githubie.
Na samym początku należy zainicjalizować moduł.
A wywoływanie zapytania SQL jest bardzo proste. Poniżej jest przykład zapisania do csv wszystkich obiektów w bazie danych, które stworzyliśmy dla schematu 'my':
Oczywiście można wywołać polecenie z pliku, gdzie znajduję się zapytanie sql:
Gdy będziemy przeglądać rekordy w bardzo dużej tabeli to będziemy mogli uzyskać wyjątek z brakiem pamięci:
Błąd powyższy odnosi się do braku pamięci:
#out-lineoutput : Exception of type 'System.OutOfMemoryException' was thrown.
# + CategoryInfo : NotSpecified: (:) [out-lineoutput], OutOfMemoryException
# + FullyQualifiedErrorId : System.OutOfMemoryException,Microsoft.PowerShell.Commands.OutLineOutputCommand
Wtedy zamiast ładować wszystko do adaptera to można odczytywać z DataReader'a:
Dodatkową funkcjonalnością jest porównywanie tabel dla różnych baz danych. Poniżej jest przykład sprawdzenia czy na 2 bazach są takie same tabele z takim samym właścicielem (owner) i z taką samą ilością rekordów:
Na samym początku należy zainicjalizować moduł.
Load-OracleAssemblyes 'path_to_oracle_dataacces_dir\Oracle.DataAccess.dll'Będziemy mogli przeglądać hasła oraz przeglądać połączenia w TNS:
$secPass, $pass = Get-Password Get-TnsOracleConnectionString | Out-GridViewMożna pobrać połączenie z app.configu oraz sprawdzić wersję servera:
$connectStr = Get-ConfigConnectionString 'path_to_app_dir\App.config' DatabaseNr1 Get-OracleServerVersion $connectStr
A wywoływanie zapytania SQL jest bardzo proste. Poniżej jest przykład zapisania do csv wszystkich obiektów w bazie danych, które stworzyliśmy dla schematu 'my':
$conn = new-OracleConnection $connectStr $conn.Open() $dbObj =Get-OracleDataTable -conn $conn -sql "select * from all_objects where owner like 'my' order by last_ddl_time desc" $conn.Close() $dbObj | Export-Csv -Path 'LastChangedObjects.csv'
Oczywiście można wywołać polecenie z pliku, gdzie znajduję się zapytanie sql:
Get-OracleDataTable -file file.sql -conn $conn
Gdy będziemy przeglądać rekordy w bardzo dużej tabeli to będziemy mogli uzyskać wyjątek z brakiem pamięci:
Błąd powyższy odnosi się do braku pamięci:
#out-lineoutput : Exception of type 'System.OutOfMemoryException' was thrown.
# + CategoryInfo : NotSpecified: (:) [out-lineoutput], OutOfMemoryException
# + FullyQualifiedErrorId : System.OutOfMemoryException,Microsoft.PowerShell.Commands.OutLineOutputCommand
Wtedy zamiast ładować wszystko do adaptera to można odczytywać z DataReader'a:
$reader = Get-OracleDataReader -conn $conn -sql "select * from all_objects"
while($reader.Reader())
{
# operacja na reader
}
Dodatkową funkcjonalnością jest porównywanie tabel dla różnych baz danych. Poniżej jest przykład sprawdzenia czy na 2 bazach są takie same tabele z takim samym właścicielem (owner) i z taką samą ilością rekordów:
$connStr1 = 'Data Source=...' $connStr2 = 'Data Source=...' $conn1 = New-OracleConnection $connStr1 $conn2 = New-OracleConnection $connStr2 $allTables1 = Get-OracleSystemTables -conn $conn1 -SystemTable ALL_TABLES $allTables2 = Get-OracleSystemTables -conn $conn2 -SystemTable ALL_TABLES Compare-Object ($allTables1 | select TABLE_NAME,OWNER,NUM_ROWS) ($allTables2 | select TABLE_NAME,OWNER,NUM_ROWS) -IncludeEqual
czwartek, 31 lipca 2014
Pseudorekurencja w Oracle
Spróbowałem moich sił w oracle w wywołaniach rekurencyjnych. Skorzystałem z klauzury WITH. Tak na prawdę nie są to wywołanie rekurencyjne. Poniżej przedstawiam przykład wyświetlania wartości silni:
Jak pewnie zauważyłeś, to nie tworzysz modelu rekurencyjnego z warunkami stopu, ale model iteracyjny z warunkami początkowymi.
with Factorial (n,fact) as ( select 0 as n, 1 as fact from dual union all select n +1, (n+1)*fact from Factorial where n < 20 ) select * from Factorial ;A tutaj przykład wyliczenia liczby ruchów z postaci jawnej dla problemu wieży Hanoi:
with hanoi (n,counts) as ( select 1 as n, 1 as counts from dual union all select n +1, POWER(2,n+1) -1 from hanoi where n < 20 ) select * from hanoi ;Wśród standardowych przykładów rekurencji zawsze musi wystąpić ciąg Fibonacciego. Zapytanie wyświetlające liczbę oraz wartość fibonacciego dla tej liczb, wygląda następująco:
with fibonacci (n,fib, fibadd) as ( select 0 as n, 0 as fib, 1 as fibadd from dual union all select n +1, fibadd, (fib + fibadd) from fibonacci where n < 100 ) select n,fib from fibonacci ;
Jak pewnie zauważyłeś, to nie tworzysz modelu rekurencyjnego z warunkami stopu, ale model iteracyjny z warunkami początkowymi.
poniedziałek, 28 lipca 2014
Produktywne makra Visual Studio
Chcę Ci przedstawić 2 makra, które pomagają mi w pisaniu kodu. Pierwsze makro dodaje nawiasy okrągłe do zaznaczonego tekstu w edytorze. Super się sprawdza makro dla kodu w PowerShell czy dla FSharp. Poniższy kod wystarczy zapisać w makrze VS:
Shift+CTRL+A, Shift+CTRL+B,
Równie dobrze można było dodać nawiasy ostrokątne do edycji plików xml.
Drugim makrem jest asercja sprawdzająca czy wartość jest nullem:
Shift+CTRL+A, Shift+CTRL+A
Te 2 makra u mnie się sprawdzają. Jeśli masz swój pomysł na makro to podziel się nim.
Public Module BracketIt
Public Sub AddBrackets()
Dim s As Object = DTE.ActiveWindow.Selection()
If s.Text.StartsWith("(") And s.Text.EndsWith(")") Then
s.Text = s.Text.Substring(1, s.Text.Length - 2)
Else
s.Text = "(" + s.Text + ")"
End If
End Sub
End Module
Skrótem klawiszowym do wywołania tego makra mam ustawione na:Shift+CTRL+A, Shift+CTRL+B,
Równie dobrze można było dodać nawiasy ostrokątne do edycji plików xml.
Drugim makrem jest asercja sprawdzająca czy wartość jest nullem:
Public Module AddAssert
Public Sub IsNotNull()
Dim s As Object = DTE.ActiveWindow.Selection()
Dim assertStr As String = "Assert.IsNotNull"
If s.Text.Length > 0 Then
s.Text = assertStr + "(" + s.Text + ")"
Else
s.Text = assertStr
End If
End Sub
End Module
A skrótem klawiszowym jest:Shift+CTRL+A, Shift+CTRL+A
Te 2 makra u mnie się sprawdzają. Jeśli masz swój pomysł na makro to podziel się nim.
niedziela, 27 lipca 2014
Count lines of code
CLOC to tool do wyliczenia liczby plików różnych formatów oraz wylicza ile jest tych linijek kodu oraz komentarzy w tych plikach.Jest to tool, bardzo prosty w działaniu. Zacząłem pisać wrapper na PowerShella.
Pierwsza funkcja już jest:
Pierwsza funkcja już jest:
function Get-Loc
{
param($location="."
)
$commandLine = "$location --csv"
$clocResult = & $clocPath $location --csv
$indexOfLineCsvToParse = [array]::IndexOf($clocResult, "")
if($indexOfLineCsvToParse -lt 0)
{
throw "cant get empty line in cloc result"
}
$csvToparse = $clocResult[$indexOfLineCsvToParse..($clocResult.Length)]
$csvFeed = ConvertFrom-Csv $csvToparse -Delimiter ',' | select files,language,blank,comment,code
return $csvFeed | %{
return New-Object PSObject -prop @{
Files = [int]($_.files);
Language = $_.language;
Blank = [int]($_.blank);
Comment = [int]($_.comment);
Code = [int]($_.code);
}
}
}
Aby wyświetlić statystykę projektów to wystarczy wywołać:Get-Loc "C:\ProjectDir"Skrypt jest dostępny w github'a.
Subskrybuj:
Posty (Atom)













