Costruire una macchina BI 2012
04 maggio 12 04.52 | abenedetti | with no comments

Installare e configurare una macchina con tutto lo stack BI?

Qui un documento che spiega, passo - passo, il setup di un completo standalone Microsoft Business Intelligence Server.

  • Install the SharePoint 2010 bits + SP1 but do not configure
  • Install PowerPivot for SharePoint, with the SQL database instance. The SQL Server database engine will be installed (as a named instance) during this part of the setup. Note that there will be no “default” instance for the DB engine in this setup.
  • Run the PowerPivot Configuration tool, which creates the SharePoint farm
  • Install SQL Analysis Services Tabular Mode, SQL Reporting Services (including the SharePoint Add-in), and SQL Analysis Services Multidimensional Mode.
  • Configure the SharePoint BI Service Applications; PeformancePoint, Excel Services, Reporting Services
  • Create 2 SharePoint sites; BI site, powerpivot site
  • Add BISM and Report Builder content types to the SharePoint sites
OLTP, YTD, QTD e funzioni analitiche
24 aprile 12 02.56 | abenedetti | with no comments

Il motore relazionale di SQL Server, come noto, non è nato / ottimizzato per effettuare analisi analitiche su moli di dati “importanti”.

Possono esserci però degli scenari in cui, per i motivi più disparati, è impossibile utilizzare il motore OLAP e quindi essere costretti a risolvere con T-SQL un’interrogazione analitica come, ad esempio, un calcolo YTD (Year-To-Date: consuntivo alla data a partire da inizio anno) e QTD (Quarter to Date: consuntivo alla data a partire da inizio trimestre).

SQL Server 2012, tra le varie novità, porta all’interno del motore relazionale alcune funzioni in grado di aiutarci a risolvere analisi come questa.

Vediamo con due esempi (utilizzando il database AdventureWorks2012) come potremmo risolvere questo calcolo con ciò che avevamo a disposizione prima di SQL Server 2012 e, subito dopo, con le funzionalità del nuovo motore, utilizzando le caratteristiche messe a disposizione dalla clausola OVER.

Questo il risultato di entrambi gli scenari:

image

Con SQL Server 2008 (utilizzando alcune CTE per rendere più leggibile e mantenibile il codice) la soluzione può essere quella di:

  • crearsi un calendario di appoggio (una tabella calendar, tipica negli scenari di analisi)
  • recuperare le informazioni (in questo caso economiche) che vogliamo utilizzare per il calcolo
  • effettuare una join del resultset con se stesso per un’operazione di running total
set statistics io on
set nocount on
 
use [AdventureWorks2012]
go
 
With tYear as
(
    select 2005 as year
    union all
    select 2006 as year
    union all
    select 2007 as year
    union all
    select 2008 as year
    union all
    select 2009 as year
    union all
    select 2010 as year
),
tMonth As
(   
 Select 'Q1' Quarter, 1 Month
 Union All
 Select 'Q1', 2
 Union All
 Select 'Q1', 3
 Union All
 Select 'Q2', 4   
 Union All
 Select 'Q2', 5   
 Union All
 Select 'Q2', 6   
 Union All
 Select 'Q3', 7    
 Union All
 Select 'Q3', 8   
 Union All
 Select 'Q3', 9   
 Union All
 Select 'Q4', 10   
 Union All
 Select 'Q4', 11   
 Union All
 Select 'Q4', 12   
),
tCalendar as
(
    select * from tYear cross join tMonth
) ,
tSalesAmount as
(
    SELECT sum(subtotal) as amount,
    year(orderdate) as _year, month(orderdate) as _month
    FROM sales.SalesOrderHeader tCalendar
    group by year(orderdate), month(orderdate)
),
tCalendarSalesAmount as
(
    select s.Year, s.Month, s.Quarter, tSalesAmount.amount from tCalendar s
    join tSalesAmount on tSalesAmount._year = s.Year and tSalesAmount._month = s.month 
)
 
Select  [Year],
        [Quarter],
        [Month],
        [Amount],
        (
            Select    SUM(b.Amount)
            FROM    tCalendarSalesAmount AS b
            WHERE    
                a.Year = b.year AND 
                ((b.[Month] - 1) / 3) = ((a.[Month] - 1) / 3) AND 
                b.[Month] <= a.[Month]
        ) AS QTD,
        (
            Select SUM(b.Amount)
            FROM    tCalendarSalesAmount AS b
            WHERE    a.Year = b.year AND 
            b.[Month] <= a.[Month]
        ) AS YTD
FROM    tCalendarSalesAmount AS a
ORDER BY [Year],
        [Quarter],
        [Month]

Con SQL Server 2012 la stessa interrogazione diventa molto più semplice da scrivere e molto meno dispendiosa in termini di tempi di analisi e di attività di I/O:

SET STATISTICS IO ON
SET NOCOUNT ON
 
USE [AdventureWorks2012]
GO
 
;WITH t0
AS (
    SELECT 
        sum(subtotal) AS subtotal, 
        year(orderdate) AS _year, 
        month(orderdate) AS _month, 
        CASE month(orderdate)
            WHEN 1
                THEN 'Q1'
            WHEN 2
                THEN 'Q1'
            WHEN 3
                THEN 'Q1'
            WHEN 4
                THEN 'Q2'
            WHEN 5
                THEN 'Q2'
            WHEN 6
                THEN 'Q2'
            WHEN 7
                THEN 'Q3'
            WHEN 8
                THEN 'Q3'
            WHEN 9
                THEN 'Q3'
            WHEN 10
                THEN 'Q4'
            WHEN 11
                THEN 'Q4'
            WHEN 12
                THEN 'Q4'
            END AS _quarter
    FROM sales.SalesOrderHeader
    GROUP BY year(orderdate), month(orderdate)
    )
SELECT 
    _year AS [Year], _quarter AS [Quarter], 
    _month AS [Month], subtotal AS Amount, 
    SUM(subtotal) OVER (PARTITION BY _year, _quarter ORDER BY _year, _month 
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS QTD, 
    SUM(subtotal) OVER (PARTITION BY _year ORDER BY _year, _month 
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS YTD
FROM t0
ORDER BY _year, _month
GO

Le differenze tra le due analisi?

Direi molto evidenti:

  1. Table 'SalesOrderHeader'. Scan count 79, logical reads 76545
  2. Table 'SalesOrderHeader'. Scan count 1, logical reads 689
SQL Server Data Tools
02 aprile 12 11.27 | abenedetti | with no comments

I SQL Server Data Tools (SSDT) sono una delle novità più interessanti di SQL Server 2012 (ne ho giusto parlato qualche giorno fa).

Oltretutto sono free ;-)

SSDT è lo strumento che sostituisce il “vecchio” BIDS, consente di sviluppare connessi o disconessi ai propri database su piattaforme: SQL Server 2005, SQL Server 2008, SQL Server 2008 R2, SQL Server 2012 e SQL Azure.

Per scaricare ed installare lo strumento si veda qui.

Tra l’altro, notizia di oggi, sono stati rilasciati i SSDT Power Tools.

Si veda qui.

Filed under:
Conoscere il numero di righe delle tabelle
02 aprile 12 08.16 | abenedetti | 1 comment(s)

Il post, in realtà, dovrebbe titolarsi: chi non ha mai avuto la necessità di effettuare il conteggio delle righe presenti su tutte le tabelle di un determinato database?

Tralasciando un inefficientissimo “SELECT COUNT(*) …” su ogni tabella (quale potrebbe essere il senso di fare una completa scansione di ogni tabella quando possiamo ottenere la stessa informazione in maniera più… intelligente?), vediamo quale può essere il miglior modo possibile (inteso in termini di maggior velocità di risposta e minor stress possibile) per rispondere alla domanda.

Mi concentro su due modalità che, a mio avviso, rispondono al meglio.

La prima, che vedo spesso utilizzata, è a dir la verità un po’ costosa in termini di I/O a causa dell’utilizzo della DMV [dm_db_index_physical_stats]:

SET NOCOUNT ON
SET STATISTICS IO ON
 
SELECT schema_name(t.schema_id) AS [Schema]
    ,t.NAME AS [Table]
    ,[record_count] AS [Nr rows]
FROM [sys].[dm_db_index_physical_stats](DB_ID(), NULL, NULL, NULL, 'SAMPLED') d
JOIN sys.tables t ON t.object_id = d.object_id
WHERE [index_id] IN (
        0
        ,1
        )
    AND alloc_unit_type_desc = 'IN_ROW_DATA'
ORDER BY [Schema]
    ,[Table]

La seconda, quella che preferisco:

SET NOCOUNT ON
SET STATISTICS IO ON
 
SELECT s.NAME AS [Schema]
    ,t.NAME AS [Table]
    ,ps.row_count
FROM sys.tables t
INNER JOIN sys.partitions p ON p.OBJECT_ID = t.OBJECT_ID
INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
INNER JOIN sys.dm_db_partition_stats ps ON t.object_id = ps.object_id
WHERE t.is_ms_shipped = 0
    AND p.index_id IN (1,0)
GROUP BY s.NAME
    ,t.NAME
    ,ps.row_count
ORDER BY [Schema]
    ,[Table]
SQL Server 2012 links
07 marzo 12 01.32 | abenedetti | 1 comment(s)

Come ha scritto Davide qui è da poco disponibile la versione RTM di SQL Server 2012.

Lascio un altro paio di link che ritengo molto interessanti:

  • SQL Server Data Tools RTW – qui
  • SQL Server 2012 Data Mining Add-ins for Office 2010 – qui
Filed under: , ,
Un calendario “al volo”
23 febbraio 12 11.17 | abenedetti | with no comments

A beneficio di tutte quelle occasioni in cui può servire un calendario (una semplice tabella con tutte le date presenti in un periodo)…

 
USE tempdb
GO
 
DECLARE @dateStart DATE = '20120101'
DECLARE @dateEnd DATE = '20121231'
 
;WITH CTE_DatesTable
 AS
 (
 SELECT @dateStart AS [date]
 UNION ALL
 SELECT DATEADD(dd, 1, [date])
 FROM CTE_DatesTable
 WHERE DATEADD(dd, 1, [date]) <= @dateEnd
 )
SELECT [date] FROM CTE_DatesTable 
OPTION (MAXRECURSION 0)
GO
 
image
Filed under: , ,
SQL Server Instant Initialization
22 febbraio 12 11.25 | abenedetti | 3 comment(s)

Le performance dei miei database sono, da sempre, uno dei miei pallini…

E, da sempre, ricordo che sono molti i dettagli che contribuiscono a realizzare sistemi performanti.

A partire dalla versione 2005 di SQL Server è possibile utilizzare una funzionalità, chiamata “Instant Initialization”, basata sul file system NTFS.

Per poterla utilizzare il sistema operativo deve necessariamente essere un Windows Server 2003 o superiore / Windows XP Professional o superiore.

Questa funzionalità, sostanzialmente, evita la “zero initialization” invocata:

  • alla creazione di un file (creazione di un database, aggiunta di file a database esistenti),
  • ad ogni attività di autogrow (che sia manuale o automatica),
  • al restore di un database.

Essendo la pratica di avere pre-allocazioni di files una best practice per evitare la frammentazione dei file, poter sfruttare un’inizializzazione istantanea (e quindi risparmiare tempo) diventa una caratteristica sicuramente interessante.

Per poter sfruttare questa caratteristica, il servizio SQL deve essere abilitato al “Perform Volume Maintenance Tasks” (di default gli amministratori sono abilitati a questa policy).

Poichè il mio servizio SQL gira con un account di bassi privilegi creato ad-hoc (altra best practice), per poter sfruttare la funzionalità, devo modificare la policy locale tramite il “Local Group Policy Editor”:

  • Computer Configuration
  • Windows Settings
  • Security Settings
  • Local Policies
  • User Rights Assignment

image

Aggiungo, quindi, il mio account …

image

… e riavvio il servizio SQL.

 

Effettuo un test di performance utilizzando questo script per creare un database che abbia un file di dati di 5GB:

DECLARE @t time(3) = SYSDATETIME()
 
CREATE DATABASE Test
ON  PRIMARY  
(
    NAME = N'Test', 
    FILENAME = N'C:\Test\Test.mdf', 
    SIZE = 5GB, 
    FILEGROWTH = 100MB
) 
LOG ON  
(
    NAME = N'Test_log', 
    FILENAME = N'C:\Test\Test.ldf', 
    SIZE = 1MB,  
    FILEGROWTH = 10MB
) 
 
SELECT DATEDIFF(ms, @t, CAST(SYSDATETIME() AS time(3))) 
    AS FileInitialization 
GO

Di seguito il tempo senza instant initialization (quindi eseguito prima di aggiungere l’utente del servizio SQL alla policy):

image

Con instant initialization:

image

Sottolineo che questo meccanismo coinvolge soltanto i file di dati (MDF e NDF), non il transaction log.

Quando viene utilizzato il meccanismo di inizializzazione istantanea?

  • creazione di un database
  • aumento della dimensione di un database esistente
  • creazione tempdb ad ogni riavvio del servizio
  • esecuzione di autogrow
  • durante le operazioni di restore di un db (pre-allocazione dello spazio necessario)

Chiudo il post con altri appunti:

  • il meccanismo NON è abilitato by default
  • NON è una caratteristica solo della versione Enterprise di SQL Server
  • il Transaction Log non può essere inizializzato istantaneamente a causa della sua natura “circolare”

Uno dei meccanismi per verificare l’abilitazione (o meno) di questa funzionalità, possiamo sfruttare il comando whoami / priv tramite la procedura estesa xp_cmdshell (attenzione!):

-- ATTENZIONE: abilito la xp_cmdshell !
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
GO
EXEC sp_configure 'xp_cmdshell', 1
RECONFIGURE
GO
---------------------------------------------
USE tempdb
GO
 
CREATE TABLE whoami 
(
    info varchar(2000)
)
GO
 
INSERT whoami
EXEC xp_cmdshell 'whoami /priv'
GO
 
SELECT * FROM whoami
WHERE info LIKE '%volume%'
GO
 
DROP TABLE whoami 
GO
 
-- Disabilito la xp_cmdshell:
EXEC sp_configure 'show advanced options', 1
RECONFIGURE
GO
EXEC sp_configure 'xp_cmdshell', 0
RECONFIGURE
GO
---------------------------------------------

Un’altra analisi di performance può essere fatta sui tempi di autogrow generati automaticamente dal sistema:

/* Creo database per test di performance */
USE MASTER
GO
 
CREATE DATABASE Test
ON  PRIMARY  
(
    NAME = N'Test', 
    FILENAME = N'C:\Test\Test.mdf', 
    SIZE = 3MB, 
    FILEGROWTH = 1MB
) 
LOG ON  
(
    NAME = N'Test_log', 
    FILENAME = N'C:\Test\Test.ldf', 
    SIZE = 1MB,  
    FILEGROWTH = 10MB
) 
GO
 
/* Creo una tabella di appoggio */
USE test
GO
 
CREATE TABLE bigData
(
    id INT PRIMARY KEY IDENTITY(1,1),
    chunk CHAR(8000)
)
GO
 
 
/* 
genero autogrow nel database 
inserendo 50.000 righe
e visualizzo il tempo trascorso
*/
SET NOCOUNT ON
DECLARE @t time(3) = SYSDATETIME()
 
DECLARE @i INT
SET @i = 0
WHILE @i < 10000
BEGIN
    INSERT bigData 
        VALUES 
            ('data 1...'),
            ('data 2...'),
            ('data 3...'),
            ('data 4...'),
            ('data 5...')
    SET @i = @i + 1
END
 
SELECT DATEDIFF(ms, @t, CAST(SYSDATETIME() AS time(3))) 
    AS [Time] 
GO


/*
Num di Autogrow Event
--------------- -------------------
391             Data File Auto Grow

Tempo senza instant initialization: 10568
Tempo con instant initialization: 9378
*/

Per approfondire l’argomento (ma non solo) credo che un’ottima lettura possa essere il whitepaper “Physical Database Storage Design” disponibile qui.

Microsoft Support Lifecycle
13 febbraio 12 03.14 | abenedetti | with no comments

Il post non tratta direttamente di SQL Server, o forse si… Sorriso

Ciascun prodotto ha un suo ciclo di vita che termina con la fine del supporto (se esiste, esteso) fornito da Microsoft.

Qui, ad esempio, le informazioni relative a SQL Server 2000.

Nota: non è supportata la migrazione da SQL Server 2000 a SQL Server 2012… e la data si avvicina… occhio…

SchemaBinding e tempo perso
31 gennaio 12 03.03 | abenedetti | with no comments

E un bel giorno alcuni report smisero di funzionare…

 

Faccio un passo indietro, ma lo scenario appena descritto è già molto chiaro.

Reports che accedono ad un db, lo interrogano andando direttamente sulle tabelle e ne presentano i dati.

 

Cosa succede se viene modificato il nome di una colonna utilizzata dal report?

Il report smette di funzionare, of course.

Ok, la domanda: abbiamo modo di dichiarare l’esistenza di una dipendenza di una certa struttura dati (ad esempio una tabella) rispetto a qualche altro oggetto (ad esempio un report)?

Si, utilizzando delle viste che, grazie all’opzione SCHEMABINDING, possano informare SQL Server di controllare ogni azione che verrà fatta sulle strutture fisiche sottostanti.

In questo modo, se la modifica invalida la vista, l’operazione di alter (o di drop) table fallisce (ed i nostri report continuano a funzionare).

Righe duplicate, righe univoche e Physloc
16 gennaio 12 06.08 | abenedetti | with no comments

Chi si è trovato, almeno una volta, di fronte a tabelle modellate male? ( quante mani alzate … :-) )

Ad esempio, di fronte a tabelle con righe duplicate, qualcosa come (un esempio stupido):

USE tempdb;
GO

CREATE TABLE test(
             col1 varchar( 3 ) , 
             col2 varchar( 3 ));
GO

INSERT INTO test
VALUES( 'aaa' , 'bbb' ) , 
      ( 'aaa' , 'bbb' ) , 
      ( 'aaa' , 'bbb' );
GO

SELECT
       *
  FROM test; 
GO

image

Come posso cancellare le righe che sono duplicate?

Direttamente, apparentemente, non posso…

Cosa intendo? Intendo dire che, per poter recuperare univocamente ciascuna riga, ho la necessità di “costruire” qualcosa che mi renda identificabile ciascuna tupla dalle altre.

Potrei usare la funzione ROW_NUMBER, ad esempio:

SELECT
       ROW_NUMBER( )
       OVER( ORDER BY col1 DESC )AS rowNum , 
       *
FROM test;
image

Oppure, prendendo spunto da qui, potrei usare la colonna virtuale [Physloc] in questo modo:

SELECT
       %%Physloc%% AS Physloc , 
       col1 , 
       col2
  FROM test;
GO
image

In questo modo, quindi, ottenendo il binary( 8 ) che rappresenta il PageID (primi 4 bytes), FileID (2 bytes) e Slot (2 bytes) ho sicuramente un’informazione univoca per ciascuna riga.

Datapage, physical storage location e fn_PhysLocFormatter
16 gennaio 12 10.10 | abenedetti | 1 comment(s)

In alcuni casi potrebbe essere utile conoscere la posizione fisica delle nostre righe.

In sostanza poter conoscere quale sia il “contenitore” che mantiene al suo interno la nostra informazione, le datapages che contengono i dati.

USE tempdb;
GO

CREATE TABLE anagrafica(
             idAnagrafica smallint , 
             cognome varchar( 35 ) , 
             nome varchar( 35 ));
GO

INSERT INTO anagrafica
VALUES( 1 , 'aaa' , 'bbb' ) , 
      ( 2 , 'ccc' , 'ddd' ) , 
      ( 3 , 'eee' , 'fff' );
GO

SELECT
       *
  FROM anagrafica; 
GO
image

A questo punto mettiamo il naso dentro le strutture che abbiamo creato:

SELECT
      %%Physloc%% as Physloc,
       sys.fn_PhysLocFormatter( %%Physloc%% )AS SysStorageLocation , 
       idAnagrafica, cognome, nome
  FROM anagrafica;

image

La “pseudo” colonna [Physloc] ci permette di vedere l’indirizzo univoco di ciascuna riga.

Grazie alla funzione (non documentata) fn_PhysLocFormatter possiamo formattare questo indirizzo in modo da visualizzare l’indirizzo fisico della riga.

Il risultato viene esposto come:

  • file num
  • datapage
  • slot

Una volta che otteniamo queste info, possiamo vedere le nostre pagine grazie al comando DBCC PAGE passando:

  • dbname / dbid
  • file num
  • page num
  • livello di informazioni che vogliamo (printopt)

Dove il “livello di informazioni” può essere:

  • 0 = header
  • 1 = header and hex dump for rows
  • 2 = header and the page dump
  • 3 = header and detail row information
DBCC TRACEON( 3604 );
DBCC PAGE('TempDB',1,114,2)
DBCC TRACEOFF( 3604 );
GO
image
SQL Server Consolidation Guidance
10 gennaio 12 07.23 | abenedetti | with no comments

Giusto per non perdere il link giusto, un withepaper molto interessante :-)

SQL Server Consolidation Guidance: qui.

The goal of this white paper is to provide a framework for choosing among virtualization, multi-database, and multi-instance consolidation strategies for SQL Server Database Engine OLTP applications.

SQL Server 2012 Filetable e Fulltext
09 gennaio 12 04.42 | abenedetti | with no comments

SQL Server 2012 aggiunge, alla funzionalità di FILESTREAM (tempo fa ne avevo parlato qui), la possibilità di interagire direttamente con le API di Windows, quindi consente ad un’applicazione di copiare dei files (e/o folder) in un path e lasciare al database engine tutta la sua gestione.

Diciamo che è il pezzo che mancava… :-)

Le cose interessanti, poi, sono diverse. Ne cito, per iniziare, due:

  • costruire dei trigger che intercettino copie di file “non voluti” e quindi intervenire “al volo”
  • gestire un catalogo fulltext e, quindi, indicizzare il contenuto dei files copiati e gestiti

Vediamo come fare…

/*
Enable and Configure FILESTREAM
http://msdn.microsoft.com/en-us/library/cc645923(v=sql.110).aspx

EXEC sp_configure Filestream_Access_level , 2;
RECONFIGURE;
GO
*/

USE MASTER;
GO

IF DB_ID( 'FileTableDB' )IS NOT NULL
    BEGIN
        DROP DATABASE FileTableDB;
    END;
GO

/*
Creazione del database
con FILESTREAM
*/
CREATE DATABASE FileTableDB ON PRIMARY
( 
    NAME = FileTableDB , 
    FILENAME = 'C:\SQL2012\db\FileTableDb\FileTableDB_Data.mdf' 
    ) , 
    FILEGROUP FileTableDBFS CONTAINS 
        FILESTREAM
        ( 
            NAME = FileTableDBFS , 
            FILENAME = 'C:\SQL2012\db\FileTableDb\FileTableDbFileStream'
        )
    LOG ON
    ( 
        NAME = FileTableDBLog , 
        FILENAME = 'C:\SQL2012\db\FileTableDb\FileTableDB_Log.ldf' 
    );
GO


/*
Specifico il livello di accesso non transazionale a livello di database
Questo per poter ottenere un file handle senza la necessità di una transazione
*/
ALTER DATABASE FileTableDB 
    SET FILESTREAM 
    ( 
    NON_TRANSACTED_ACCESS = FULL, DIRECTORY_NAME = N'FileTableDbFileStream'
    )
GO

/*
Passo al database utente
*/
USE FileTableDB;
GO

/*
Creo una tabella FileTable
*/
CREATE TABLE tblFileTableTest AS FileTable
WITH
(
        FileTable_Directory = 'tblFileTableTest',
        FileTable_Collate_Filename = database_default
)
GO

/*
A questo punto posso aprire una share di rete composta come:
\\nomeMacchina\nomeistanza\directoryName\fileTableDirectory

Quindi, nel mio caso, posso aprire:
\\Abalien\sql2012\FileTableDbFileStream\tblFileTableTest
*/

/* Ecco come posso recuperare il path alla document library: */
SELECT FileTableRootPath() AS 'FileTablePath' 
GO
image
/*
vedo la tabella (vuota)
*/
SELECT *
  FROM tblFileTableTest;
GO


/*
Aggiungo un trigger alla tabella perchè, per ipotesi,
voglio interrompere operazioni di copia se contenenti file TXT
*/
CREATE TRIGGER dbo.ControlFileTXT ON tblFileTableTest
    FOR INSERT , UPDATE
AS
IF EXISTS( SELECT 1
             FROM inserted
             WHERE inserted.file_type = 'txt' )
    BEGIN
        ROLLBACK;
    END;
GO

A questo punto posso fare drag & drop di files all’interno del mio folder e ritrovarli in tabella:

image

Oppure posso provare a copiare dei TXT e ricevere un errore di copia:

image

 

Fatto questo abilitiamo l’indicizzazione FULLTEXT sul nostro database e sulla nostra tabella:

/* Abilitiamo catalogo e indicizzazione FULLTEXT */
CREATE FULLTEXT CATALOG filetableCat;
GO

CREATE UNIQUE INDEX ui_document ON tblFileTableTest( stream_id );
GO

CREATE FULLTEXT INDEX ON tblFileTableTest
( file_stream TYPE COLUMN name LANGUAGE 1033 )
KEY INDEX ui_document ON filetableCat WITH CHANGE_TRACKING AUTO;
GO


/* Vediamo lo stato di indicizzazione */
SELECT CASE FULLTEXTCATALOGPROPERTY( 'filetableCat' , 'Populatestatus' )
       WHEN 0 THEN 'Idle'
       WHEN 1 THEN 'Full population in progress '
       WHEN 2 THEN 'Paused'
       WHEN 3 THEN 'Throttled'
       WHEN 4 THEN 'Recovering'
       WHEN 5 THEN 'Shutdown'
       WHEN 6 THEN 'Incremental population in progress'
       WHEN 7 THEN 'Building index'
       WHEN 8 THEN 'Disk is full. Paused.'
       WHEN 9 THEN 'Change tracking'
       END AS populateStatus , 
       CASE FULLTEXTCATALOGPROPERTY( 'filetableCat' , 'AccentSensitivity' )
       WHEN 0 THEN 'Accent insensitive'
       WHEN 1 THEN 'Accent sensitive'
       END AS AccentSensitivity , 
       FULLTEXTCATALOGPROPERTY( 'filetableCat' , 'ItemCount' )AS ItemCount;
GO

 

Attenzione: ricordiamoci che NON tutti i tipi di file sono immediatamente indicizzabili (per alcuni, come il PDF, serve installare il suo iFilter)

Per vedere le estensioni conosciute:

SELECT * FROM sys.fulltext_document_types;

image

Una volta copiati files nella nostra tabella filetable possiamo farne una ricerca testuale con una query come:

/* Test di ricerca */
DECLARE @SearchWord nvarchar( 300 );
SET @SearchWord = '"enabling end users"';

SELECT name
  FROM dbo.tblFileTableTest FT 
  JOIN CONTAINSTABLE
    ( dbo.tblFileTableTest , file_stream , @SearchWord ) AS T 
        ON FT.stream_id = T.[key];
GO
Upgrade Assistant for SQL Server 2012
09 gennaio 12 01.42 | abenedetti | with no comments

E’ disponibile in download qui il nuovo Upgrade Assistant per SQL Server 2012.

La pagina wiki del tool qui.

Database mail e dimensione query result
08 gennaio 12 03.41 | abenedetti | with no comments

Facendo un pò di manutenzione su alcuni job dei nostri server, trovo questo errore su uno di essi:

Executed as user: XXXXXXXXXXXXXx. File attachment or query results size exceeds allowable value of 1000000 bytes. [SQLSTATE 42000] (Error 22050).  The step failed.

Il job in questione effettua una semplice query ed invia, via mail, il suo risultato.

Qualcosa come questo:

EXEC msdb.dbo.sp_send_dbmail
@profile_name = 'XXXXXXXXXXXXxx',
@recipients = 'XXXXXXXXXXXXX',
@query = 'use myDatabase; SELECT … ‘,
@subject = '…..',
@query_result_no_padding =0;

 

Evidentemente (il messaggio è chiaro) il resultset è cresciuto a tal punto da eccedere la dimensione massima spedibile.

Come risolvo?

Apro la configurazione della mail (sul nodo “Management del SSMS, “Database Mail”, tasto dx, “Configure Database Mail”), “next” sulla prima videata, e poi “view or change system parameters”:

image

Quindi modifico il valore a mia descrizione:

image

Filed under: , , ,
More Posts Next page »

This Blog

Syndication