How to count number of rows in SQL Server database? - sql-server

I am trying to count the number of unread messages in my DB Table but is proving to be very difficult. I've even read tutorials online but to no avail.
What I'm doing should be simple.
Here's what I'm trying to do:
COUNT NUMBER OF ROWS IN NOTIFICATIONSTABLE
WHERE USERID = #0 AND MESSAGEWASREAD = FALSE
Can somebody please point me in the right direction? Any help will be appreciated.
Thank you
#helper RetrievePhotoWithName(int userid)
{
var database = Database.Open("SC");
var name = database.QuerySingle("select FirstName, LastName, ProfilePicture from UserProfile where UserId = #0", userid);
var notifications = database.Query("SELECT COUNT(*) as 'counter' FROM Notifications WHERE UserID = #0 AND [Read] = #1", userid, false);
var DisplayName = "";
if(notifications["counter"] < 1)
{
DisplayName = name["FirstName"] + " " + name["LastName"];
}
else
{
DisplayName = name["FirstName"] + ", you have " + notifications["counter"] + " new messages.";
}
<img src="#Href("~/Shared/Assets/Images/" + name["ProfilePicture"] + ".png")" id="MiniProfilePicture" /> #DisplayName
database.Close();
}

SELECT COUNT(*) FROM NotificationsTable WHERE
UserID = #UserID AND MessageWasRead = 0;
Sql Count Function
Okay so this is based on what I think should be done. I don't know the underlying types, so it is going to be my best guess.
var notifications = database.QuerySingle("Select COUNT(*) as NumRecs....");
if((int)notifications["NumRecs"] > 0)) .......
I changed the query for notifications to QuerySingle. You don't need a recordest, you only need a scalar value, so that should (hopefully remove your problem with the implicit conversion in the equals you were having.
I would also check to see if your database object implements IDisposable (place it in a using statement if so) as you are calling close, and this won't actually call close (I know it's not dispose but it might have dispose too) if you encounter and exception before the close function is called.

int unreadMessageCount = db.Query("SELECT * FROM Notification WHERE UserId=#0 AND Read=#1",UserId,false).Count();
string displayname = name["FirstName"] + " " + name["LastName"] + unreadMessageCount>0?",you have " + unreadMessageCount :"";

Related

How to get row data where column = ? also applying limit to rows?

I am getting an error on applying a raw query in sq-lite.The error is
Caused by: android.database.sqlite.SQLiteException: near "Limit": syntax error (code 1): ,
while compiling: SELECT * FROM quiz_questions WHERE Levels = "Level 1"+ Limit 2+
The query SELECT * FROM quiz_questions WHERE Levels = "Level 1" works and gives me multiple rows but when i am applying limit to query, it gives error.
This is code for it.
public ArrayList<Question> getLockedLevels(ArrayList<String> Level) {
ArrayList<Question> questionList = new ArrayList<>();
db = getReadableDatabase();
for (int i = 0; i < Level.size(); i++)
{
String levelID = Level.get(i);
String SELECT_TABLE_QUERY = "SELECT * FROM " + TABLE_NAME + " WHERE " + COLUMN_LEVEL + " = \"" + levelID + "\"+ Limit 1";
Cursor cursor = db.rawQuery(SELECT_TABLE_QUERY, null);
if (cursor.moveToFirst()) {
do {
Question question = new Question();
question.setmLevels(cursor.getString(cursor.getColumnIndex(COLUMN_LEVEL)));
question.setmLevels_lockmanager(cursor.getInt(cursor.getColumnIndex(COLUMN_LEVEL_LOCKMANAGER)));
questionList.add(question);
} while (cursor.moveToNext());
}
cursor.close();
}
return questionList;
}
Try
String SELECT_TABLE_QUERY = "SELECT * FROM " + TABLE_NAME + " WHERE " + COLUMN_LEVEL + " = '" + levelID + "' Limit 1";
That should resovle to :-
SELECT * FROM quiz_questions WHERE Levels = 'Level 1' Limit 1
However, the above is subject to potential SQL Injection.
Using :-
String levelID = Level.get(i);
String SELECT_TABLE_QUERY = "SELECT * FROM " + TABLE_NAME + " WHERE " + COLUMN_LEVEL + "=? Limit 1";
Cursor cursor = db.rawQuery(SELECT_TABLE_QUERY, new String[]{levelID });
would remove the potential for SQL Injection and is therefore considered the more correct way.
Alternatively you could utilise the convenience query method that not only protects against SQL Injection but also generates much of the SQL.
So you could use :-
for (int i = 0; i < Level.size(); i++) {
Cursor cursor = db.query(TABLE_NAME,null,COLUMN_LEVEL + "=?",new String[]{Level.get(i)},null,null,null,"1");
while (cursor.moveToNext()) {
Question question = new Question();
question.setmLevels(cursor.getString(cursor.getColumnIndex(COLUMN_LEVEL)));
question.setmLevels_lockmanager(cursor.getInt(cursor.getColumnIndex(COLUMN_LEVEL_LOCKMANAGER)));
questionList.add(question);
}
cursor.close();
}
See SQliteDatabase - query
The above also uses the shorter while(cursor.moveToNext) { ..... } for looping through the Cursor (if there are no rows then the loop is not entered as moveToNext will return false if the move cannot be made)

Transaction Management By ##Transactional Annotation

I have one requirement in which i have to insert record in one table and update the record in another table.
There are 100 records for which i have to do above insertion and updation.
But If any record fails due to any error in insertion or updation only that record should be rollback not others.Means if There are 100 records and 60 gets success and 61 gets fails due to any error then only 61 records should be rolled back not all other 60 records.
I am using Spring boot JPA and #Transactional Annotation.But if put this annotation on starting of my method,Then in case of any error it roll back all records.
I am attaching my code below.Please suggest how i can achieve this.
In below code i have persistRecord() method which have all my code for insertion and updation.I have not marked this with #Transactional annotation insteed below method is annotated with #Transactional annotation
logger.debug("insertSalesTargetData method called of service class");
String userid = (String) webSession.getAttribute("username");
logger.debug("Userid :" + userid);
HashMap<String, String> persistRecordStatus = new HashMap<String, String>();
Query qCountCheck = em.createNativeQuery(QueryConstant.CHECK_SALES_TARGET_DATA_QUERY);
List<Object> existingRecordList = null;
persistRecordStatus.put("TOTAL_RECORD", String.valueOf(xlsRowslist.size()));
int failedRecordCount = 0;
int successRecordCount = 0;
for (int i = 0; i < xlsRowslist.size(); i++) {
ArrayList rowList = xlsRowslist.get(i);
// try {
double weekNoDouble = Double.parseDouble((String) rowList.get(0));// Week No
String fromDate = (String) rowList.get(1);// fromDate
String toDate = (String) rowList.get(2);// toDate
double catCodeDouble = Double.parseDouble((String) rowList.get(3));// catCode
int catCode = (int) catCodeDouble;
int weekNo = (int) weekNoDouble;
String target = (String) rowList.get(4);// target
String salesGoalId = fromDate + toDate + catCode;
salesGoalId = salesGoalId.replace("-", "");
// Check if the sales goal id already exist in the database or not
qCountCheck.setParameter(1, salesGoalId);// SALES_GOAL_ID
existingRecordList = qCountCheck.getResultList();
logger.debug("Count List Size " + existingRecordList.size());
if (existingRecordList != null && existingRecordList.size() > 0) {
if (existingRecordList.get(0) != null) {
BigDecimal row = (BigDecimal) existingRecordList.get(0);
// try {
logger.debug("Persisting record no " + i);
persistRecord(row, salesGoalId, target, fromDate, toDate, userid, catCode, weekNo);
logger.debug("Record no " + i + " persisted");
persistRecordStatus.put("SUCCESS_RECORD", String.valueOf(successRecordCount++));
/*
* } catch (Exception e) { persistRecordStatus.put("FAILED_RECORD",
* String.valueOf(failedRecordCount++)); }
*/
}
} else {
persistRecordStatus.put("FAILED_RECORD",String.valueOf(failedRecordCount++));
}
/*
* } catch (Exception e) { logger.debug("Exception in processing record no " + i
* + " " + e.toString()); persistRecordStatus.put("FAILED_RECORD",
* String.valueOf(failedRecordCount++)); }
*/
}
return persistRecordStatus;
}
You are doing some kind of batch updates ?
In batch processing, you treat "failures" by putting all stuff involved (e.g. the input record, an error code, ...) on an error store for subsequent review by some user. And you commit that along with all the "successful" updates.
Alternatively, put each insert/update pair in its own transaction. (and still alternatively, if your DBMS / transactional framework supports savepoints, take a savepoint after each insert/update pair and upon failure, rollback to the last savepoint and then commit (and figure out a way to not lose the remaining 40 unprocessed entries from your input). In either case, don't come complaining if you get performance trouble.
There is no having things both ways in transactional batch processing.

slow SQLite read speed (100 records a second)

I have a large SQLite database (~134 GB) that has multiple tables each with 14 columns, about 330 million records, and 4 indexes. The only operation used on the database is "Select *" as I need all the columns(No inserts or updates). When I query the database, the response time is slow when the result set is big (takes 160 seconds for getting ~18,000 records).
I have improved the use of indexes multiple times and this is the fastest response time I got.
I am running the database as a back-end database for a web application on a server with 32 GB of RAM.
is there a way to use RAM (or anything else) to speed up the query process?
Here is the code that performs the query.
async.each(proteins,function(item, callback) {
`PI[item] = []; // Stores interaction proteins for all query proteins
PS[item] = []; // Stores scores for all interaction proteins
PIS[item] = []; // Stores interaction sites for all interaction proteins
var sites = {}; // a temporarily holder for interaction sites
var query_string = 'SELECT * FROM ' + organism + PIPE_output_table +
' WHERE ' + score_type + ' > ' + cutoff['range'] + ' AND (protein_A = "' + item + '" OR protein_B = "' + item '") ORDER BY PIPE_score DESC';
db.each(query_string, function (err, row) {
if (row.protein_A == item) {
PI[item].push(row.protein_B);
// add 1 to interaction sites to represent sites starting from 1 not from 0
sites['S1AS'] = row.site1_A_start + 1;
sites['S1AE'] = row.site1_A_end + 1;
sites['S1BS'] = row.site1_B_start + 1;
sites['S1BE'] = row.site1_B_end + 1;
sites['S2AS'] = row.site2_A_start + 1;
sites['S2AE'] = row.site2_A_end + 1;
sites['S2BS'] = row.site2_B_start + 1;
sites['S2BE'] = row.site2_B_end + 1;
sites['S3AS'] = row.site3_A_start + 1;
sites['S3AE'] = row.site3_A_end + 1;
sites['S3BS'] = row.site3_B_start + 1;
sites['S3BE'] = row.site3_B_end + 1;
PIS[item].push(sites);
sites = {};
}
}
The query you posted uses no variables.
It will always return the same thing: all the rows with a null score whose protein column is equal to its protein_a or protein_b column. You're then having to filter all those extra rows in Javascript, fetching a lot more rows than you need to.
Here's why...
If I'm understanding this query correctly, you have WHERE Score > [Score]. I've never encountered this syntax before, so I looked it up.
[keyword] A keyword enclosed in square brackets is an identifier. This is not standard SQL. This quoting mechanism is used by MS Access and SQL Server and is included in SQLite for compatibility.
An identifier is something like a column or table name, not a variable.
This means that this...
SELECT * FROM [TABLE]
WHERE Score > [Score] AND
(protein_A = [Protein] OR protein_B = [Protein])
ORDER BY [Score] DESC;
Is the same as this...
SELECT * FROM `TABLE`
WHERE Score > Score AND
(protein_A = Protein OR protein_B = Protein)
ORDER BY Score DESC;
You never pass any variables to the query. It will always return the same thing.
This can be seen here when you run it.
db.each(query_string, function (err, row) {
Since you're checking that each protein is equal to itself (or something very like itself), you're likely fetching every row. And it's why you have to filter all the rows again. And that is one of the reasons why your query is so slow.
if (row.protein_A == item) {
BUT! WHERE Score > [Score] will never be true, a thing cannot be greater than itself except for null! Trinary logic is weird. So only if Score is null can that be true.
So you're returning all the rows whose score is null and the protein column is equal to protein_a or protein_b. This is a lot more rows than you need, I guess you have a lot of rows with null scores.
Your query should incorporate variables (I'm assuming you're using node-sqlite3) and pass in their values when you execute the query.
var query = " \
SELECT * FROM `TABLE` \
WHERE Score > $score AND \
(protein_A = $protein OR protein_B = $protein) \
ORDER BY Score DESC; \
";
var stmt = db.prepare(query);
stmt.each({$score: score, $protein: protein}, function (err, row) {
PI[item].push(row.protein_B);
...
});

Searching in database from a form returning empty input

I want to do search in my database using some fields filled by a form
but if some fields are left empty i don't want to include them
which kind of query can help me in achieving this??
Currently i am using a query like:
Select * from DATABASE where COLUMN='form_input';
but as my form will return empty it will try and select rows which have null entries but rather i want to this time see a result of all rows in database i.e i want to invalidate the filter by COLUMN='form_input'
As we do not know your server side scripting language -
The psheuducode should be -
if(request['form_input']!=null)
Select * from DATABASE where COLUMN='form_input';
else
Select * from DATABASE;
Also If there are many fields for form_input then we can design
our code something like -
String wherequery = "";
if(request['form_input1']!=null)
{
wherequery = wherequery + " COLUMN='form_input1' ";
}
if(request['form_input2']!=null)
{
wherequery = wherequery + " And "
wherequery = wherequery + " COLUMN='form_input2' ";
}
if(request['form_input3']!=null)
{
wherequery = wherequery + " And "
wherequery = wherequery + " COLUMN='form_input3' ";
}
....
And so on
....
String selectQuery = "";
if(wherequery == "")
{
selectQuery = "Select * from TABLE";
}
else
{
selectQuery = "Select * from TABLE where" + wherequery ;
}
execute (selectQuery);
Please note we are using pseudo code here. We can take the form inputs and concatenate query for each input which is not null.
If we find the concatenated string as blank string, we will select the full table.
Otherwise
we will select with the where clause query.
Hope, this help you out.

Easy way to convert exec sp_executesql to a normal query?

When dealing with debugging queries using Profiler and SSMS, its pretty common for me to copy a query from Profiler and test them in SSMS. Because I use parameterized sql, my queries are all sent as exec sp_executesql queries.
exec sp_executesql
N'/*some query here*/',
N'#someParameter tinyint',
# someParameter =2
I'll take this and convert it into a normal query for ease of editing (intellisense, error checking, line numbers, etc):
DECLARE #someParameter tinyint
SET #someParameter = 2
/*some query here*/
Of course, the bigger and more complex the query, the harder to do this. And when you're going back and forth multiple times, it can be a pain in the ass and soak up lots of time.
Is there an easy (e.g., macro command) way to convert muh executesql into something more convenient?
I spent a little time making an simple script that did this for me. It's a WIP, but I stuck a (very ugly) webpage in front of it and it's now hosted here if you want to try it:
http://execsqlformat.herokuapp.com/
Sample input:
exec sp_executesql
N'SELECT * FROM AdventureWorks.HumanResources.Employee
WHERE ManagerID = #level',
N'#level tinyint',
#level = 109;
And the output:
BEGIN
DECLARE #level tinyint;
SET #level = 109;
SELECT * FROM AdventureWorks.HumanResources.Employee
WHERE ManagerID = #level
END
The formatting of the actual SQL statement once I've plucked it from the input is done using the API at http://sqlformat.appspot.com
I was looking for something similar so I use this in LinqPad, just copy sp_executesql statement to the clipboard and run the code in LinqPad. It outputs the SQL statement.
void Main()
{
ConvertSql(System.Windows.Forms.Clipboard.GetText()).Dump();
}
private static string ConvertSql(string origSql)
{
string tmp = origSql.Replace("''", "~~");
string baseSql;
string paramTypes;
string paramData = "";
int i0 = tmp.IndexOf("'") + 1;
int i1 = tmp.IndexOf("'", i0);
if (i1 > 0)
{
baseSql = tmp.Substring(i0, i1 - i0);
i0 = tmp.IndexOf("'", i1 + 1);
i1 = tmp.IndexOf("'", i0 + 1);
if (i0 > 0 && i1 > 0)
{
paramTypes = tmp.Substring(i0 + 1, i1 - i0 - 1);
paramData = tmp.Substring(i1 + 1);
}
}
else
{
throw new Exception("Cannot identify SQL statement in first parameter");
}
baseSql = baseSql.Replace("~~", "'");
if (!String.IsNullOrEmpty(paramData))
{
string[] paramList = paramData.Split(",".ToCharArray());
foreach (string paramValue in paramList)
{
int iEq = paramValue.IndexOf("=");
if (iEq < 0)
continue;
string pName = paramValue.Substring(0, iEq).Trim();
string pVal = paramValue.Substring(iEq + 1).Trim();
baseSql = baseSql.ReplaceWholeWord(pName, pVal);
}
}
return baseSql;
}
public static class StringExtensionsMethods
{
/// <summary>
/// Replaces the whole word.
/// </summary>
/// <param name="s">The s.</param>
/// <param name="word">The word.</param>
/// <param name="replacement">The replacement.</param>
/// <returns>String.</returns>
public static String ReplaceWholeWord(this String s, String word, String replacement)
{
var firstLetter = word[0];
var sb = new StringBuilder();
var previousWasLetterOrDigit = false;
var i = 0;
while (i < s.Length - word.Length + 1)
{
var wordFound = false;
var c = s[i];
if (c == firstLetter)
if (!previousWasLetterOrDigit)
if (s.Substring(i, word.Length).Equals(word))
{
wordFound = true;
var wholeWordFound = true;
if (s.Length > i + word.Length)
{
if (Char.IsLetterOrDigit(s[i + word.Length]))
wholeWordFound = false;
}
sb.Append(wholeWordFound ? replacement : word);
i += word.Length;
}
if (wordFound) continue;
previousWasLetterOrDigit = Char.IsLetterOrDigit(c);
sb.Append(c);
i++;
}
if (s.Length - i > 0)
sb.Append(s.Substring(i));
return sb.ToString();
}
}
I spent a little time and created a small modification of Matt Roberts / Wangzq solutions without DECLAREs section, you can try it on .NET Fiddle or download LINQPad 5 file.
Input:
exec sp_executesql N'UPDATE MyTable SET [Field1] = #0, [Field2] = #1',N'#0 nvarchar(max) ,#1 int',#0=N'String',#1=0
Output:
UPDATE MyTable SET [Field1] = N'String', [Field2] = 0
Code:
using System;
using System.Linq;
using System.Text.RegularExpressions;
public class Program
{
public static void Main()
{
var sql = #"exec sp_executesql N'UPDATE MyTable SET [Field1] = #0, [Field2] = #1',N'#0 nvarchar(max) ,#1 int',#0=N'String',#1=0";
Console.WriteLine(ConvertSql(sql));
}
public static string ConvertSql(string origSql)
{
var re = new Regex(#"exec*\s*sp_executesql\s+N'([\s\S]*)',\s*N'(#[\s\S]*?)',\s*([\s\S]*)", RegexOptions.IgnoreCase); // 1: the sql, 2: the declare, 3: the setting
var match = re.Match(origSql);
if (match.Success)
{
var sql = match.Groups[1].Value.Replace("''", "'");
//var declare = match.Groups[2].Value;
var setting = match.Groups[3].Value + ',';
// to deal with comma or single quote in variable values, we can use the variable name to split
var re2 = new Regex(#"#[^',]*?\s*=");
var variables = re2.Matches(setting).Cast<Match>().Select(m => m.Value).ToArray();
var values = re2.Split(setting).Where(s=>!string.IsNullOrWhiteSpace(s)).Select(m => m.Trim(',').Trim().Trim(';')).ToArray();
for (int i = variables.Length-1; i>=0; i--)
{
sql = Regex.Replace(sql, "(" + variables[i].Replace("=", "")+")", values[i], RegexOptions.Singleline);
}
return sql;
}
return #"Unknown sql query format.";
}
}
Another solution which replaces the parameter values directly in the query
(not exactly what you asked for but it might prove useful to others):
https://code.msdn.microsoft.com/windowsdesktop/spExecuteSql-parser-1a9cd7bc
I goes from:
exec sp_executesql N'UPDATE Task SET Status = #p0, Updated = #p1 WHERE Id = #p2 AND Status = #p3 AND Updated = #p4',N'#p0 int,#p1 datetime,#p2 int,#p3 int,#p4 datetime',#p0=1,#p1='2015-02-07 21:36:30.313',#p2=173990,#p3=2,#p4='2015-02-07 21:35:32.830'
to:
UPDATE Task SET Status = 1, Updated = '2015-02-07 21:36:30.313' WHERE Id = 173990 AND Status = 2 AND Updated = '2015-02-07 21:35:32.830'
which makes it easier to understand.
The console application on that page can be used by passing a file parameter or copying the sp_executesql in the clipboard, running the app and then pasting the resulting SQL from the clipboard.
Update:
An SQL formatter can also be added to that solution for easier readability:
http://www.nuget.org/packages/PoorMansTSQLFormatter/
newSql = ConvertSql(Clipboard.GetText());
var formattedSql = SqlFormattingManager.DefaultFormat(newSql);
Clipboard.SetText(formattedSql);
You can use this Azur data studio extension. it based on #Matt Roberts repo.
https://github.com/PejmanNik/sqlops-spexecutesql-to-sql/releases/tag/0.0.1
Sql Prompt got this feature recently (2017-02-06). Select the text and look for "Inline EXEC" in the context menu. Gotta love Prompt :)
I am not aware of an existing Add-In that can do this. But you could create one :)
A few regular expressions and some string concatenation and after that sell it to Vinko and other souls looking for this functionality.
If you're feeling like diving into this, here is some information on creating an SSMS addin:
http://sqlblogcasts.com/blogs/jonsayce/archive/2008/01/15/building-a-sql-server-management-studio-addin.aspx
I faced with this problem too and wrote simple application for solving it - ClipboardSqlFormatter. This is a tray application that listens clipboard input events and tries to detect and convert dynamic sql to static sql.
Anything you need is to copy dynamic sql (from sql profiler for example) and paste to text editor - pasted sql will be a static sql :)
For example, if copied sql is:
exec sp_executesql N' SELECT "obj"."CreateDateTime", "obj"."LastEditDateTime" FROM LDERC
"doc" INNER JOIN LDObject "obj" ON ("doc"."ID" = "obj"."ID") LEFT OUTER JOIN LDJournal
"ContainerID.jrn" ON ("doc"."JournalID" = "ContainerID.jrn"."ID") WHERE ( "doc"."ID"
= #V0 AND ( "doc"."StateID" <> 5 AND "ContainerID.jrn"."Name" <> ''Hidden journal''
) ) ',N'#V0 bigint',#V0=6815463'
then pasted sql will be:
SELECT "obj"."CreateDateTime"
,"obj"."LastEditDateTime"
FROM LDERC "doc"
INNER JOIN LDObject "obj" ON ("doc"."ID" = "obj"."ID")
LEFT OUTER JOIN LDJournal "ContainerID.jrn" ON ("doc"."JournalID" = "ContainerID.jrn"."ID")
WHERE (
"doc"."ID" = 6815463
AND (
"doc"."StateID" <> 5
AND "ContainerID.jrn"."Name" <> 'Hidden journal'
)
)
Conclusion: I note this still gets a little attention so I'll add details here for what my eventual solution was.
It turns out that nothing beats doing it for yourself. I created a simple console app that parsed my stored procedure and spit out what I wanted. By adding it to the list of external tools, and passing the current filename as an argument, I could use the following to strip out and rearrange what I needed.
In use, I'd add a new sql file, paste in the sql, save it, then run the external tool. After it completes, the IDE asks me to reload the file. Poof, no more stored procedure.
I do note that this may not work with every executesql statement, so you'll have to modify if it does not meet your needs.
class Program
{
const string query = "query";
const string decls = "decls";
const string sets = "sets";
static void Main(string[] args)
{
try
{
var text = File.ReadAllText(args[0]);
if(string.IsNullOrEmpty(text))
{
Console.WriteLine("File is empty. Try saving it before using the hillbilly sproc decoder");
}
var regex = new Regex(#"exec sp_executesql N'(?<" + query + ">.*)',N'(?<" + decls + ">[^']*)',(?<" + sets + ">.*)", RegexOptions.Singleline);
var match = regex.Match(text);
if(!match.Success || match.Groups.Count != 4)
{
Console.WriteLine("Didn't capture that one. Shit.");
Console.Read();
return;
}
var sb = new StringBuilder();
sb.Append("DECLARE ").AppendLine(match.Groups[decls].Value);
foreach(var set in match.Groups[sets].Value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
sb.Append("SET ").AppendLine(set);
sb.AppendLine(match.Groups[query].Value.Replace("''", "'"));
File.WriteAllText(args[0], sb.ToString());
}
catch(Exception ex)
{
Console.WriteLine("S*t blew up, yo");
Console.WriteLine(ex.ToString());
Console.WriteLine("Press a key to exit");
Console.Read();
}
}
}
Here is simple UI that i use to inspect NHibernate queries. Some regex, parsing and sqlformat.org API to beautify sql is used.
<html>
<head>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
$(function(){
$("#btn-format").on("click", () =>{
var insql = $("#textarea-in").val();
var regex = new RegExp("exec sp_executesql N'(?<command>.+?(?='(,N'$)?))'(,\s*N'(?<types>.+?(?=',))',\s*(?<vals>.+))?");
var groups = insql.replace(/\n|\r/g, "").match(regex).groups;
var outsql = "";
if (groups.types)
{
var types = groups.types.match(/#[^\s]+ \w+(\([\w\d,]+\))?/g);
for (const typeDeclaration of types) {
outsql = outsql + 'declare ' + typeDeclaration + '\n';
}
outsql = outsql + '\n';
for (const setVal of groups.vals.split(',')) {
outsql = outsql + 'set ' + setVal + '\n';
}
outsql = outsql + '\n';
}
$.ajax({
url: 'https://sqlformat.org/api/v1/format',
type: 'POST',
dataType: 'json',
crossDomain: true,
data: {
sql: groups.command, reindent: 1
},
success: (data) => {
outsql = outsql + data.result;
$("#textarea-out").val(outsql);
},
error: () =>{
outsql = outsql + '-- No format happened. See browser console for details \n';
outsql = outsql + groups.command;
$("#textarea-out").val(outsql);
}
});
})
});
</script>
</head>
<body>
<textarea id="textarea-in" style="width: 100%; height: 48%;" class="form-control" placeholder="type 'exec sp_executesql...' here"></textarea>
<br/>
<button id="btn-format">Format</button>
<br/>
<textarea id="textarea-out" style="width: 100%; height: 48%;" class="form-control"></textarea>
</body>
Test in Fiddle
Note: Will not work, if you have single quotes in query
I have improved one of previous answers particularly refining regular expression in order to support queries without parameters. Here is my option in form of PowerShell script, which uses Windows clipboard both as input and as output:
$regex = "(?s)^exec sp_executesql N'(?<query>.*?[^'])'(?:,N'(?<decls>.*?[^'])',(?<sets>.*))?$"
$inputText = [string](Get-Clipboard)
if (!($inputText -match $regex)) {
return
}
$resultBuilder = [System.Text.StringBuilder]::new()
if ($matches["decls"]) {
$resultBuilder.AppendLine("DECLARE $($matches["decls"])")
$matches["sets"].Split(',') | ForEach-Object {
$resultBuilder.AppendLine("SET $_");
}
}
$resultBuilder.AppendLine($matches["query"].Replace("''", "'"));
Set-Clipboard $resultBuilder.ToString()

Resources