niedziela, 31 października 2010

collation

Czasami polskie znaki (ą,ę,ć…) przysparzają sporo problemów, znalezienie np. loginu na podstawie nazwiska jest sporym wyzwaniem.
Stworzenie do tego celu wyszukiwania pełnotekstowego mija się z celem.
Możemy zrobić .NET (CLR) lub funkcję używającą REPLACE.
Ja proponuję sztuczkę z wykorzystaniem COALESCE

Tworzymy tabelkę

USE tempdb
go
CREATE TABLE #coalesce  (txt VARCHAR (20))
go
INSERT #coalesce
        SELECT 'jazn zolta ges cma'
UNION SELECT 'jaźń żółtą gęś ćma'
go

Sprawdzamy jakie mamy kodowanie

SELECT name,collation_name
FROM sys.all_columns
WHERE object_id = object_id('#coalesce')

name      collation_name
----------------------------
txt       Polish_CI_AS


Szukamy w tekście

SELECT txt FROM #coalesce
WHERE txt LIKE 'jazn zolta ges cma'

txt
--------------------
jazn zolta ges cma

(1 row(s) affected)

Możemy poszukać tak, ale to mało elastyczne rozwiązanie

SELECT txt FROM #coalesce
WHERE txt LIKE 'ja[zź][nń] [zż][oó][lł]t[aą] g[eę][sś] [cć]ma'

txt
--------------------
jazn zolta ges cma
jaźń żółtą gęś ćma

(2 row(s) affected)

Możemy zmienić kodowanie wyszukiwanego teksu

SELECT txt FROM #coalesce
WHERE txt  LIKE 'jazn zolta ges cma' COLLATE cyrillic_general_ci_as

txt
--------------------
jazn zolta ges cma
jaźń żółtą gęś ćma

(2 row(s) affected)

Wybór strony kodowania na cyrillic_general_ci_as jedną z wielu możliwości.

CREATE TABLE #test(name varchar(200), description varchar(MAX))
go
INSERT #test
SELECT *
FROM fn_helpcollations()

CREATE TABLE #wynik (kodowanie varchar(200))

DECLARE @name varchar(200)
DECLARE @sql varchar(max)
SET @sql ='SELECT txt FROM #coalesce
WHERE txt  LIKE ''jazn zolta ges cma'' COLLATE '

declare cursorek cursor for
SELECT name FROM #test
OPEN cursorek
FETCH NEXT FROM cursorek INTO
@name
WHILE @@fetch_status =0
BEGIN
EXEC (@sql+@name)
IF @@ROWCOUNT=2
INSERT #wynik SELECT @name

FETCH NEXT FROM cursorek INTO
@name
END
CLOSE cursorek
DEALLOCATE cursorek

SELECT COUNT(*) FROM #wynik

-----------
291

piątek, 29 października 2010

Przechwycenie tabeli tymczasowej

Tabele tymczasowe służą jako obiekty pomocnicze do tymczasowego przechowywania danych, kontenerem dla tych tabel jest baza TEMPDB.
Baza tworzona jest poprzez kopiowanie bazy model przy każdym restarcie SQL Server. Tabele tymczasowe tworzymy takimi samymi metodami jak zwykłe tabele (CREATE TABLE, poprzez SELECT… INTO).
Na tabelach tymczasowych można zakładać klucze z wyjątkiem klucza obcego (FOREGIN KEY), oraz wszystkie constraints.
Tabele tymczasowe domyślnie używają collation bazy tempdb, jeżeli są tworzone przy pomocy INTO pola mają collation tabeli źródłowej.
Ze względu na obszar dostępu do tabeli możemy je podzielić na:
TABELE LOKALNE, rozpoznawalne po prefiksie #
Widoczne w wewnątrz sesji i niszczone po zakończeniu jej.

TABELE GLOBALNE, rozpoznawalne po prefiksie ##
Widoczne we wszystkich sesjach, brak możliwości ograniczenia dostępu do nich i niszczone po zakończeniu sesji która je utworzyła.



Czy na pewno nie możemy dostać się do tabeli tymczasowe lokalnej

Możemy obejrzeć jakie tabele tymczasowe zostały stworzone

Tworzymy tabelkę na SPID 52

SELECT @@SPID as spid

CREATE TABLE #tmp (pole varchar(20))
go
INSERT #tmp
SELECT 'abcdefghijk'
go 3
--------------------------

spid
------
53

(1 row(s) affected)

Beginning execution loop

(1 row(s) affected)

(1 row(s) affected)

(1 row(s) affected)
Batch execution completed 3 times.

Sprawdzamy

select count(*) from #tmp
------------------------
3

Łączymy się innym spidem

SELECT @@SPID spid

select * from #tmp
go
------------------------

spid
------
51

(1 row(s) affected)

Msg 208, Level 16, State 0, Line 3
Invalid object name '#tmp'.

Tabela jest niewidoczna, możemy ja zobaczyć zapytaniem

select name,id from  tempdb.sys.sysobjects
where xtype ='U'
and name like '#tmp%'
---------------------------

name                                                                                                                             id
-------------------------------------------------------------------------------------------------------------------------------- -----------
#tmp________________________________________________________________________________________________________________00000000A457 77360186

Do nazwy tabeli dodawane są “podkreślenia” i unikalny numer który jest inkrementacją globalną bazy tempdb. Powodują unikalność nazwy w globalnym znaczeniu, Nazwa tabeli składa się z 128 znaków:
pierwszy znak #
116 znaków na nazwę tabeli, jeżeli jest krótsza system "dopełnia" ją znakiem"_"
12 znaków na numer identyfikujacy sesję.

Jeżeli na spid 51 założymy tabelę #temp będzie ona w bazie pod inną nazwą

CREATE TABLE #tmp (pole varchar(20))
go
INSERT #tmp
SELECT 'abcdefghijk'
go 3
select name,id from  tempdb.sys.sysobjects
where xtype ='U'
and name like '#tmp%'
---------------------------

name                                                                                                                             id
-------------------------------------------------------------------------------------------------------------------------------- -----------
#tmp________________________________________________________________________________________________________________00000000A457 77360186
#tmp________________________________________________________________________________________________________________00000000A458 93360243

Możemy zobaczyć ile wierszy jest w takiej tabeli

SELECT  @@SPID spid,t.name,       
t.[object_id],
p.[rows]FROM   
tempdb.sys.tables t
JOIN    tempdb.sys.partitions p       
ON  t.[object_id] = p.[object_id]
WHERE   t.name LIKE N'#tmp%';

-------------
spid   name                      object_id   rows
------ ---------------------------------------------
51     #tmp__ …. _____00000000A457 77360186    3



Możemy znależć miejsce na dysku w którym znajdujż się dane z tabeli i odczytać dane. Poniżej moja procedura która przeczytać z dysku dane i złożyć je powrotem w tabelę.


CREATE PROCEDURE rk_dajtemp @nazwa varchar(500)
AS

      SET NOCOUNT on
      DECLARE @sql varchar(500)
    DECLARE @PageFID int
    DECLARE @PagePID int
    DECLARE @objectid int     
      CREATE TABLE #dbcc_ind(ROWID int IDENTITY(1,1) PRIMARY KEY,
                            PageFID         smallint,
                            PagePID         int,
                            IAMFID          int,
                            IAMPID          int,
                            ObjectID        int,
                            IndexID         int,
                            PartitionNumber bigint,
                            PartitionID     bigint,
                            Iam_Chain_Type  varchar(80),
                            PageType        int,
                            IndexLevel      int,
                            NexPageFID      int,
                            NextPagePID     int,
                            PrevPageFID     int,
                            PrevPagePID     int)
                           
    CREATE TABLE #kolumny(ColumnID int PRIMARY KEY,
                            Name     varchar(800))
                         
      CREATE TABLE #dbcc_page(ROWID        int IDENTITY(1,1) PRIMARY KEY,
                            ParentObject varchar(500),
                            Object       varchar(500),
                            Field        varchar(500),
                            Value        varchar(Max))

    CREATE TABLE #pomoc(ROWID     int PRIMARY KEY,
                            Page      varchar(100),
                            Slot      varchar(300),
                            Object    varchar(300),
                            FieldName varchar(300),
                            Value     varchar(6000))

      select @objectid=id from  tempdb.sys.sysobjects
      where xtype ='U'
      and name like @nazwa+'________________%'

    SELECT @sql = 'DBCC IND(tempdb, ' +
                   CONVERT(varchar(20), @objectid) +
                   ', 1) WITH NO_INFOMSGS'
   
     INSERT INTO #kolumny
       SELECT ColID,Name
       FROM tempdb..syscolumns
       WHERE id =@objectid

    DBCC TRACEON(3604) WITH NO_INFOMSGS

      INSERT INTO #dbcc_ind
    EXEC (@sql)

    DECLARE kursorek CURSOR FOR
    SELECT PageFID, PagePID FROM #dbcc_ind
    WHERE PageType = 1

    OPEN kursorek
      FETCH NEXT FROM kursorek INTO @PageFID, @PagePID

    WHILE @@FETCH_STATUS = 0
    BEGIN
      DELETE #dbcc_page
     
      SELECT @sql = 'DBCC PAGE (tempdb, '  +
                     CONVERT(varchar(20), @PageFID) +
                     ',' +
                     CONVERT(varchar(20), @PagePID) +
                     ', 3) WITH TABLERESULTS, NO_INFOMSGS '
     
      INSERT INTO #dbcc_page
      EXEC (@sql)
     
      DELETE FROM #dbcc_page
       WHERE Object NOT LIKE 'Slot %'
          OR Field = ''
          OR Field IN ('Record Type', 'Record Attributes')
          OR ParentObject in ('PAGE HEADER:')
     
      INSERT INTO #pomoc
      SELECT ROWID, cast(@PageFID as varchar(20)) + ':' + CAST(@PagePID as varchar(20)),
      ParentObject, Object, Field, Value FROM #dbcc_page

      FETCH NEXT FROM kursorek INTO @PageFID, @PagePID
    END
   
    CLOSE kursorek
    DEALLOCATE kursorek
   
    SELECT @sql = '
    SELECT ' +
    STUFF(CAST((SELECT ',[' + Name + ']'
                  FROM #kolumny
                 ORDER BY ColumnID FOR XML PATH('')) AS varchar(MAX)), 1,1,'')+'
    FROM (SELECT CONVERT(varchar(20), Page) + CONVERT(varchar(500),Slot)
    p, FieldName x_FieldName_x, Value x_Value_x FROM #pomoc) Tab
    PIVOT(MAX(Tab.x_Value_x) FOR Tab.x_FieldName_x IN( '
    + STUFF((SELECT ',[' + Name + ']' FROM #kolumny order by ColumnID for xml path('')), 1,1,'') + ' )
    ) AS pvt'

    EXEC (@sql)



Wywołujemy procedurę

SELECT @@SPID spid
execute rk_dajtemp '#tmp'

--------------------------


spid
------
51

pole
------------
abcdefghijk
abcdefghijk
abcdefghijk