using a temporary table with NHibernate - nhibernate

I'm trying to make what seems to be an advanced use of NHibernate with sql server functions.
I'm using NHibernate's ICriteria interface to supply paging, sorting and filtering for my listviews.
one of business objects is an aggregation of items from 3 different tables.
in order to do this aggregation in the DB I've used a transact-sql function, accepting parameters.I'm using the IQuery interface returned by session.GetNamedQuery to invoke the function. but in order to use the paging/filtering/sorting code i'd like to use the ICriteria interface.
in order to achieve that I considered:
Opening a new transaction
calling the function which will create a global temporary table (instead of returning the result as it does now)
somehow alter the NHibernate mapping so it applies for the temporary table (not sure I can do that, also this has to be specific to the scope where I create the transaction...)
run the query on the new table using the new mapping, using the ICriteria interface
Delete the temporary table
so a number of questions:
can you suggest an alternative?
is it possible to replace the table in an NHibernate mapping on run time, locally for a specific code scope?
how costly would it be to generate and dispose of the temporary table?

Can you replace the function with a view? This view could aggregate the 3 tables, be mapped by NHibernate, and easily paged/sorted/filtered.

I wrote a blog post which shows how you can do exactly this here: pagination for performance intensive queries using nhibernate and sql server temporary tables

I use a table variable inside of an Hibernate SQL query using SQL 2012 Dialect, like below.
For my scenario I couldn't get a particular parameterized, paginated query to work in under 17 seconds no matter how I constructed the filter. By using a table variable, the time to grab page N with a page size of 1000 resulted in sub-second response.
All the magic happens in the SQL below that
creates a table variable - declare #temporderstatus table
selects the rows into the table variable - insert into #temporderstatus filtered by the sql in sqlfilter string
selects the data from the table variable to return to NHibernate ordered so that pagination returns a predictable result set - select OrderNum, Customer... from #temporderstatus ORDER BY StatusCodeChangedDate, OrderNum
uses the NHibernate paging functions to insert the necessary ROW/OFFSET statements using SQL Server 2012 dialect - SetFirstResult((pagination.Page - 1) * pagination.PageSize).SetMaxResults( pagination.PageSize )
Unlike a temp table, a table variable cleans up after itself, so it is ideal for NHibernate scenarios where you have a web server that is serving paginated requests.
Here is my code...
var session = _sessionManager.GetStatelessSession();
StringBuilder sqlfilter = new StringBuilder(
#"FROM Orders o join OrderType ot ON o.OrderType = ot.OrderType where o.StatusDate between :fromDate and :toDate" );
var mainQuery = session.CreateSQLQuery(
$#"declare #temporderstatus table (OrderNum int not null, CustomerID int, OrderType varchar(16), Status varchar(3), StatusCodeChangedDate datetime, OrderDate datetime, DeliveryDate datetime)
insert into #temporderstatus
SELECT o.OrderNum, o.CustomerID, ot.Description AS OrderType, o.StatusCode AS Status, o.StatusCodeChangedDate, o.OrderDate, o.DeliveryDate
{sqlfilter}
select OrderNum, CustomerID, OrderType, Status, StatusCodeChangedDate, OrderDate, DeliveryDate
from #temporderstatus
ORDER BY StatusCodeChangedDate, OrderNum
");
//construct the count query
var totalCountQuery = session.CreateSQLQuery($"SELECT COUNT(1) OCount {sqlfilter} ");
totalCountQuery.AddScalar("OCount", NHibernateUtil.Int32);
totalCountQuery.SetParameter("fromDate", criteria.fromDate);
totalCountQuery.SetParameter("toDate", criteria.toDate);
var totalCountResults = totalCountQuery.UniqueResult<int>();
pagination.TotalResultCount = totalCountResults;
if (pagination.TotalResultCount == 0)
{
//no results so don't waste time doing another query
return new List<OrderDataDto>();
}
//finish constructing the main query
mainQuery.AddEntity(typeof(OrderDataDto));
mainQuery.SetParameter("fromDate", criteria.fromDate);
mainQuery.SetParameter("toDate", criteria.toDate);
var mainQueryResults = mainQuery
.SetFirstResult((pagination.Page - 1)*pagination.PageSize).SetMaxResults(pagination.PageSize);
var results = mainQueryResults.List<OrderDataDto>();
return results;

Related

Passing Array of Values into Inner SQL Server Select Statement in Node Project

In my Node batch-processor project I am using the mssql package, and I need to gather data from two different SQL databases and then basically emulate a merge operation. My question is, for SQL Server, is there a way I can use IN to pass in an array of IDs as part of the inner SELECT?
To clarify, the IDs I'll be passing in have been gathered from a separate query from a different SQL database. So what I need to do now is pass those IDs into this next query.
I've seen this kind of syntax:
SELECT DISTINCT Name
FROM Production.Product
WHERE ProductModelID IN
(SELECT ProductModelID
FROM Production.ProductModel AS pm
WHERE p.ProductModelID = pm.ProductModelID
AND Name LIKE 'Long-Sleeve Logo Jersey%');
GO
What would it look like to pass in those IDs as part of the inner SELECT statement? Is this something that can be done? Or is there a different way I should approach this?
And to clarify, I do need to do this within my Node project - so calling a stored procedure and using linked tables from my SQL Server database is not an option.
By the way, I could do something like this:
getMatchingIdRecords = async function() {
for (let sourceRecord of sourceArr) {
const matchingIdRecord = await sqlServerQueryHandler(`SELECT NoteDetailsId FROM SR_Empsheets
WHERE NoteDetailsId = ${sourceRecord.notes_detail_id}`);
if (matchingIdRecord) matchingIdRecords.push(matchingIdRecord);
}
return matchingIdRecords;
};
But this is less than ideal because I'm basically hitting the database for every record that is iterated through.
For SQL Server 2016+ you can pass a JSON Array to the query and parse it on the server. Something like:
SELECT NoteDetailsId FROM SR_Empsheets
WHERE NoteDetailsId in ( select value from openjson(#jsonArray) )
Where #jsonArray is a nvarchar(max) parameter that looks like
N'["1323","2311","1234"]'

Extract db objects dependencies from SSRS dataset with inline query (Query Type : Text)

I am dealing with a specific problem of identifying the dependent db objects for any SSRS RDL.
I have a good understanding of if any dataset have stored procedure as the query in a RDL then I can reference the associated stored procedure and get all the dependent objects (details can be found here: Different Ways to Find SQL Server Object Dependencies)
But I am looking specifically for the datasets with text query or inline query for any rdl. I am able to extract the CommandText from the XML of the rdl but I am not sure how to extract db objects like sp, table, views columns form a command text which is inline query in the rdl.
For example if I extract below query from XML commandText (this is a hypothetical query, names are not standardized in the database like vw_ for views , udf_ for functions):
-----This query serves Report ABC
SELECT DATE
,[amount]
,teamID = (SELECT TeamID FROM Sales.[getSalesPerson](r.date) as s WHERE R.[SalesPersonName] = S.[SalesPersonName])
,[channel]
,[product]
,[Item]
,r.[M_ID]
,Amount
,M.[Type]
FROM dbo.FactTable AS R
LEFT JOIN sp_Channel C ON R.[Channel_ID] = C.[Channel_ID]
LEFT JOIN Marketing.vw_M M ON R.[M_ID] = M.[M_ID]
Is there a way to identify that this query have dependent object as below:
ObjectName ObjectType
------------------------------------------
dbo.FactTable Table
sp_Channel Stored Procedure
Marketing.vw_M View
Sales.[getSalesPerson] Function
It is not easy to extract object names from an SQL command since they may be written in different ways (with/without schemas, databases name included ...)
But there are many option to extract objects from an SQL query that you can try:
Using Regular expressions, As example: You have to search for the words located after the following keywords:
TRUNCATE TABLE
FROM
UPDATE
JOIN
The following code is a C# example:
Regex regex = new Regex(#"\bJOIN\s+(?<Retrieve>[a-zA-Z\._\d\[\]]+)\b|\bFROM\s+(?<Retrieve>[a-zA-Z\._\d\[\]]+)\b|\bUPDATE\s+(?<Update>[a-zA-Z\._\d]+)\b|\bINSERT\s+(?:\bINTO\b)?\s+(?<Insert>[a-zA-Z\._\d]+)\b|\bTRUNCATE\s+TABLE\s+(?<Delete>[a-zA-Z\._\d]+)\b|\bDELETE\s+(?:\bFROM\b)?\s+(?<Delete>[a-zA-Z\._\d]+)\b");
var obj = regex.Matches(sql);
foreach(Match m in obj)
{
Console.WriteLine(m.ToString().Substring(m.ToString().IndexOf(" ")).Trim());
}
Output
Then you have to clean and join the result with the sys.objects tables from the SQL Server database.
Using a SQL parser, as example:
SQL Parser
SQL Parser - Code Project
You can refer to the following very helpful links for additional information:
Regular expression to find all table names in a query
Parsing SQL code in C#
If your reports are connecting to SQLServer and you have access you could try to get the execution plan with SET SHOWPLAN_XML ON and parse it.
Relevant thread for the parsing:extracting-data-from-sql-servers-xml-execution-plan

Dynamic query with columns and table names from multiple tables using SQL or PL/SQL

Lets assume (for simplicity) i have two tables:
Product
product_id
CreatedDate
LastEditedBy
EditedDate
Entity
entity_id
CreatedDate
LastEditedBy
EditedDate
Both tables have the same column names but only the ID column has a different name. I would like to run a query so that when i run it from SQL plus i just give it a parameter and it gets the data from one of the tables. In the above example i could run the query like this
#archiveHistory.sql product
#archiveHistory.sql entity
Here is my attempt but it always failed to recognise one of the columns, i.e. if i run it with product, it says entity_id does not exist. if i run it with entity it says product_id does not exist.
Note that i am using the passed in parameter on both the column selection and the table name selection.
define identifier = '&1'
Select * from (
Select case lower('&identifier')
when product then product_id
when entity then entity_id
end ID, CreatedDate, LastEditedBy, EditedDate
From &identifier
)
I think it will work if the column list in the CASE statement were all from the same table.
Questions
What do i need to do so the query ignores the column that is not relevant i.e. ignore product_id if the argument is entity
I thought about using an anonymous PL/SQL block (i.e. Begin End) but i am not sure how i can display the output without using dbms_output.put_line.
For this particular case, I think the following SQL-only solution might work best:
SELECT product_id AS the_field
, CreatedDate, LastEditedBy, EditedDate
FROM Product
WHERE LOWER('&identifier') = 'product'
UNION ALL
SELECT entity_id AS the_field
, CreatedDate, LastEditedBy, EditedDate
FROM Entity
WHERE LOWER('&identifier') = 'entity'
The query planner will pre-evaluate your '&identifier' = ... predicates, which prevents execution of the unneeded union subquery.
If that's not an option (because your real-world use-case is much more complex), there are plenty of answers on Stack Overflow already regarding the execution of dynamic SQL from PL/SQL:
Executing a dynamic sql statement into a SYS_REFCURSOR
Cursor For Loop with dynamic SQL-Statement
Binding Parameters to Oracle Dynamic SQL
You could use dynamic SQL to insert your data into a temp table, and then simply SELECT * FROM temp_table

Jet engine (Access) : Passing a list of values to a stored procedure

I am currently writing a VBA-based Excel add-in that's heavily based on a Jet database backend (I use the Office 2003 suite -- the problem would be the same with a more recent version of Office anyway).
During the initialization of my app, I create stored procedures that are defined in a text file. Those procedures are called by my app when needed.
Let me take a simple example to describe my issue: suppose that my app allows end-users to select the identifiers of orders for which they'd like details. Here's the table definition:
Table tblOrders: OrderID LONG, OrderDate DATE, (other fields)
The end-user may select one or more OrderIDs, displayed in a form - s/he just has to tick the checkbox of the relevant OrderIDs for which s/he'd like details (OrderDate, etc).
Because I don't know in advance how many OrderID s/he will select, I could dynamically create the SQL query in the VBA code by cascading WHERE clauses based on the choices made on the form:
SELECT * FROM tblOrders WHERE OrderID = 1 OR OrderID = 2 OR OrderID = 3
or, much simpler, by using the IN keyword:
SELECT * FROM tblOrders WHERE OrderID IN (1,2,3)
Now if I turn this simple query into a stored procedure so that I can dynamically pass list of OrderIDs I want to be displayed, how should I do? I already tried things like:
CREATE PROCEDURE spTest (#OrderList varchar) AS
SELECT * FROM tblOrders WHERE OrderID IN (#OrderList)
But this does not work (I was expecting that), because #OrderList is interpreted as a string (e.g. "1,2,3") and not as a list of long values. (I adapted from code found here: Passing a list/array to SQL Server stored procedure)
I'd like to avoid dealing with this issue via pure VBA code (i.e. dynamically assigning list of values to a query that is hardcoded in my application) as much as possible. I'd understand if ever this is not possible.
Any clue?
You can create the query-statement string dynamically. In SQL Server you can have a function whose return value is a TABLE, and invoke that function inline as if it were a table. Or in JET you could also create a kludge -- a temporary table (or persistent table that serves the function of a temporary table) that contains the values in your in-list, one per row, and join on that table. The query would thus be a two-step process: 1) populate temp table with INLIST values, then 2) execute the query joining on the temp table.
MYTEMPTABLE
autoincrementing id
QueryID [some value to identify the current query, perhaps a GUID]
myvalue one of the values in your in-list, string
select * from foo
inner join MYTEMPTABLE on foo.column = MYTEMPTABLE.myvalue and MYTEMPTABLE.QueryId = ?
[cannot recall if JET allows ANDs in INNER JOIN as SQL Server does --
if not, adjust syntax accordingly]
instead of
select * from foo where foo.column IN (... )
In this way you could have the same table handle multiple queries concurrently, because each query would have a unique identifier. You could delete the in-list rows after you're finished with them:
DELETE FROM MYTEMPTABLE where QueryID = ?
P.S. There would be several ways of handling data type issues for the join. You could cast the string value in MYTEMPTABLE as required, or you could have multiple columns in MYTEMPTABLE of varying datatypes, inserting into and joining on the correct column:
MYTEMPTABLE
id
queryid
mytextvalue
myintvalue
mymoneyvalue
etc

Select the last row in a SQL table

Is it possible to return the last row of a table in MS SQL Server.
I am using an auto increment field for the ID and i want to get the last one just added to join it with something else. Any idea?
Here's the code:
const string QUERY = #"INSERT INTO Questions (ID, Question, Answer, CategoryID, Permission) "
+ #"VALUES (#ID, #Question, #Answer, #CategoryID, #Permission) ";
using (var cmd = new SqlCommand(QUERY, conn))
{
cmd.Parameters.AddWithValue("#Question", question);
cmd.Parameters.AddWithValue("#Answer", answer);
cmd.Parameters.AddWithValue("#CategoryID", lastEdited);
cmd.Parameters.AddWithValue("#Permission", categoryID);
cmd.ExecuteNonQuery();
}
Not safe - could have multiple inserts going on at the same time and the last row you'd get might not be yours. You're better off using SCOPE_IDENTITY() to get the last key assigned for your transaction.
using an auto increment field ... and i want to get the last one just added to join it with something else.
The key here is "just added". If you have a bunch of different users hit the db at the same time, I don't think you want user A to retrieve the record created by user B. That means you probably want to use the scope_identity() function to get that id rather than running a query on the table again right away.
Depending on the context you might also need ##identity (would include triggers) or ident_current('questions') (limited to a specific table, but not the specific scope). But scope_identity() is almost always the right one to use.
Here's an example:
DECLARE #NewOrderID int
INSERT INTO TABLE [Orders] (CustomerID) VALUES (1234)
SELECT #NewOrderID=scope_identity()
INSERT INTO TABLE [OrderLines] (OrderID, ProductID, Quantity)
SELECT #NewOrderID, ProductID, Quantity
FROM [ShoppingCart]
WHERE CustomerID=1234 AND SessionKey=4321
Based on the code you posted, you can do something like this:
// don't list the ID column: it should be an identity column that sql server will handle for you
const string QUERY = "INSERT INTO Questions (Question, Answer, CategoryID, Permission) "
+ "VALUES (#Question, #Answer, #CategoryID, #Permission);"
+ "SELECT scope_identity();";
int NewQuestionID;
using (var cmd = new SqlCommand(QUERY, conn))
{
cmd.Parameters.AddWithValue("#Question", question);
cmd.Parameters.AddWithValue("#Answer", answer);
cmd.Parameters.AddWithValue("#CategoryID", lastEdited);
cmd.Parameters.AddWithValue("#Permission", categoryID);
NewQuestionID = (int)cmd.ExecuteScalar();
}
See my answer to another question here:
get new SQL record ID
The problem now is that you'll likely want subsequent sql statements to be in the same transaction. You could do this with client code, but I find keeping it all on the server to be cleaner. You could do that by building a very long sql string, but I tend to prefer a stored procedure at this point.
I'm also not a fan of the .AddWithValue() method — I prefer explicitly defining the parameter types — but we can leave that for another day.
Finally, it's kind of late now, but I want to emphasize that it's really better to try to keep this all on the db. It's okay to run multiple statements in one sql command, and you want to reduce the number of round trips you need to make to the db and the amount of data you need to pass back and forth between the db and your app. It also makes it easier to get the transactions right and keep things atomic where they need to be.
use
scope_identity() returns the last identity value generated in this session and this scope
ident_current() returns the last identity value generated for a particular table in any session and any scope
select ident_current( 'yourTableName' )
will return the last identity created by a different session.
Most of the time you should use scope_identity() right after an insert statement like so.
--insert statement
SET #id = CAST(SCOPE_IDENTITY() AS INT)
MSDN Link - Scope_Identity()
MSDN Link - Ident_Current
select top 1 * from yourtable order by id desc
I'm not sure of your version of SQL Server, but look for the OUTPUT clause of ther INSERT statement. You can capture a set of rows with this clause
Since the questioner is using .NET, here's a modified example of how to do it. (I removed ID from the insert list since it's autoincrement--the original example would fail. I also assume ID is an SQL int, not a bigint.)
const string QUERY = #"INSERT INTO Questions (Question, Answer, CategoryID, Permission) "
+ #"VALUES (#Question, #Answer, #CategoryID, #Permission);"
+ #"SELECT #ID = SCOPE_IDENTITY();";
using (var cmd = new SqlCommand(QUERY, conn))
{
cmd.Parameters.AddWithValue("#Question", question);
cmd.Parameters.AddWithValue("#Answer", answer);
cmd.Parameters.AddWithValue("#CategoryID", lastEdited);
cmd.Parameters.AddWithValue("#Permission", categoryID);
cmd.Parameters.Add("#ID", System.Data.SqlDbType.Int).Direction = ParameterDirection.Output;
cmd.ExecuteNonQuery();
int id = (int)cmd.Parameters["#ID"].Value;
}
EDITED: I also suggest considering LINQ to SQL instead of hand-coding SqlCommand objects--it's much better (faster to code, easier to use) for many common scenarios.
With a simple select you can do something like this:
SELECT *
FROM table_name
WHERE IDColumn=(SELECT max(IDColum) FROM table_name)