Saturday, March 26, 2016

Range sequence id generator for NHibernate

NHibernate offers 2 standard generators utilizing SQL Server sequences: 'sequence' and 'seqhilo'. The first is inefficient when saving many objects at once and the second has some problems:
1. dependency is introduced between database and components generating IDs; max_lo must be the same everywhere; if it is changed somewhere, there is no alert and id clash may be postponed until later date when it will become more difficult to fix
2. inserting rows in SQL (e.g. legacy stored procedures) becomes more difficult because hilo algorithm, which is arguably counter-intuitive and tricky, needs to be implemented in pure SQL; if implemented successfully, one more place is thus added where max_lo is defined.

Unfortunately MS SQL syntax for retrieving sequence values does not support batches, you can only get 1 value with 'next value for ...'. But there's a documented stored procedure sys.sp_sequence_get_range which does exactly what we need: leaving sequence returning exact IDs (not a value from which ID needs to be calculated using configuration stored elsewhere), it generates arbitrary number of IDs at once. So why not use it in a custom generator:



and then in hbm:

    <id name="Id" type="Int64">
      <column name="Id" not-null="true" />
      <generator class="SqlServerSequenceIdGenerator, MyAssembly">
        <param name="SequenceName">dbo.MyTableId</param>
        <param name="CacheSize">100</param>
      </generator>
    </id>
or fluent (unverified):

Identity.GeneratedBy.Custom<SqlServerSequenceIdGenerator>(b => b.AddParam("SequenceName", "dbo.MyTableId").AddParam("CacheSize", "100"));

Friday, March 25, 2016

Parameterized PowerQuery

PowerQuery is a nice self-service BI tool, except it does not support parameters for e.g. SQL queries.
With a bit of extra work this limitation can be worked around. We can keep queries parameterless and use in their places values taken from some table in the database; then a bit of VBScript would grab values from cells in the workbook reserved for parameters and insert them into that table before invoking refresh of all data connections.
To enable multiple different reports to be used by multiple users at the same time we can bind parameter values to client workstation, report name and target database.
Here you can download SQL scripts and excel macro-enabled workbook with sample implementation. Scripts create a separate database for parameters and test reporting database with sample reporting stored procedure. Workbook's 'Parameters' sheet has a predefined cell for report name, drop-down list parameters for source server and database and expandable Excel table for arbitrary list of parameters, initially containing datetime period start and end datetime parameters as a sample. To add new parameters just insert new row into the table; parameters are identified by the value in 'Code' column. 'Target database info' table is just a place for information about source data currently loaded into workbook.
After setting up test databases on localhost user would change parameter values and click "Reload data" button to apply changes instead of using "Refresh" button on "Data" ribbon. Using the latter would bypass the insertion of new parameter values and old ones would be used.
Note: sample is written for SQL Server 2012 or 2014; will need minor modifications for older versions.