Need to wildcard a XML node value method - sql

Parsing an SSIS package for documentation purposes and I want to wildcard the trap for script code in case the creator did not leave the name as "ScriptMain"
Here is the code section I have right now:
SELECT RowID as ControlFlowRowID,
CF.TaskName
,cfnodes1.x.value('./ProjectItem[#Name=''ScriptMain.cs''] [1]', 'nvarchar(max)') CSScript
,cfnodes1.x.value('./ProjectItem[#Name=''ScriptMain.vb''][1]', 'nvarchar(max)') VBScript
FROM ##tmp_SSISpkgControlFlow cf
CROSS APPLY Cf.ScriptTaskQry.nodes('.') AS cfnodes1(x)
I want to take out the dependence on ScriptMain. Any help would be great.
section of XML in question:
</ProjectItem>
<ProjectItem
Name="ScriptMain.cs"
Encoding="UTF8"><![CDATA[#region Help: Introduction to the script task
/* The Script Task allows you to perform virtually any operation that can be accomplished in
* a .Net application within the context of an Integration Services control flow.
*
* Expand the other regions which have "Help" prefixes for examples of specific ways to use
* Integration Services features within this script task. */
#endregion
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.Data.OleDb;
using System.Data.SqlClient;
using System.Xml;
using System.IO;
#endregion
namespace ST_2817a78c2b684bfc87bfd7fb00086a37
{
/// <summary>
/// ScriptMain is the entry point class of the script. Do not change the name, attributes,
/// or parent of this class.
/// </summary>
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
#region Help: Using Integration Services variables and parameters in a script
/* To use a variable in this script, first ensure that the variable has been added to
* either the list contained in the ReadOnlyVariables property or the list contained in
* the ReadWriteVariables property of this script task, according to whether or not your
* code needs to write to the variable. To add the variable, save this script, close this instance of
* Visual Studio, and update the ReadOnlyVariables and
* ReadWriteVariables properties in the Script Transformation Editor window.
* To use a parameter in this script, follow the same steps. Parameters are always read-only.
*
* Example of reading from a variable:
* DateTime startTime = (DateTime) Dts.Variables["System::StartTime"].Value;
*
* Example of writing to a variable:
* Dts.Variables["User::myStringVariable"].Value = "new value";
*
* Example of reading from a package parameter:
* int batchId = (int) Dts.Variables["$Package::batchId"].Value;
*
* Example of reading from a project parameter:
* int batchId = (int) Dts.Variables["$Project::batchId"].Value;
*
* Example of reading from a sensitive project parameter:
* int batchId = (int) Dts.Variables["$Project::batchId"].GetSensitiveValue();
* */
#endregion
#region Help: Firing Integration Services events from a script
/* This script task can fire events for logging purposes.
*
* Example of firing an error event:
* Dts.Events.FireError(18, "Process Values", "Bad value", "", 0);
*
* Example of firing an information event:
* Dts.Events.FireInformation(3, "Process Values", "Processing has started", "", 0, ref fireAgain)
*
* Example of firing a warning event:
* Dts.Events.FireWarning(14, "Process Values", "No values received for input", "", 0);
* */
#endregion
#region Help: Using Integration Services connection managers in a script
/* Some types of connection managers can be used in this script task. See the topic
* "Working with Connection Managers Programatically" for details.
*
* Example of using an ADO.Net connection manager:
* object rawConnection = Dts.Connections["Sales DB"].AcquireConnection(Dts.Transaction);
* SqlConnection myADONETConnection = (SqlConnection)rawConnection;
* //Use the connection in some code here, then release the connection
* Dts.Connections["Sales DB"].ReleaseConnection(rawConnection);
*
* Example of using a File connection manager
* object rawConnection = Dts.Connections["Prices.zip"].AcquireConnection(Dts.Transaction);
* string filePath = (string)rawConnection;
* //Use the connection in some code here, then release the connection
* Dts.Connections["Prices.zip"].ReleaseConnection(rawConnection);
* */
#endregion
/// <summary>
/// This method is called when this script task executes in the control flow.
/// Before returning from this method, set the value of Dts.TaskResult to indicate success or failure.
/// To open Help, press F1.
/// </summary>
public void Main()
{
string connectionString = "";
string filepath = Dts.Variables["varSourceFilePath"].Value.ToString();
string serverName = Dts.Variables["varServerName"].Value.ToString();
string databaseName = Dts.Variables["varDBName"].Value.ToString();
connectionString = #"Data Source=" + serverName + ";Initial Catalog=" + databaseName + ";Integrated Security=true;";
StreamReader reader = File.OpenText(filepath);
string input = null;
string abc = null;
string Col;
while ((input = reader.ReadLine()) != null)
{
abc = abc + input;
}
Col = abc.ToString();
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string queryString = "insert into [pkgStats]([PackageXML]) Values(#field1)";
SqlCommand command = new SqlCommand(queryString, connection);
command.Parameters.AddWithValue("#field1", Col.ToString());
command.ExecuteReader();
connection.Close();
}
Dts.TaskResult = (int)ScriptResults.Success;
}
#region ScriptResults declaration
/// <summary>
/// This enum provides a convenient shorthand within the scope of this class for setting the
/// result of the script.
///
/// This code was generated automatically.
/// </summary>
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
}
}]]></ProjectItem>
<ProjectItem
Name="ST_2817a78c2b684bfc87bfd7fb00086a37.csproj"
Encoding="UTF8"><![CDATA[<?xml version="1.0" encoding="utf-8"?>
UPDATE: Here is the revised SQL with the Accepted Answer applied:
select
Data.ControlFlowRowID as CF_Rowid,
ScriptName,
ScriptCode as ScriptText,
ScriptType
from
(
SELECT RowID as ControlFlowRowID,
CF.TaskName,
CASE WHEN ProjectItem.value('#Name','varchar(max)') LIKE '%.vb' THEN 'VB Script'
WHEN ProjectItem.value('#Name','varchar(max)') LIKE '%.cs' THEN 'C# SCript'
ELSE 'Other'
END AS ScriptType
,ProjectItem.value('#Name','varchar(max)') AS ScriptName
,ProjectItem.value('.','varchar(max)') AS ScriptCode
FROM ##tmp_SSISpkgControlFlow cf
CROSS APPLY Cf.ScriptTaskQry.nodes('//ProjectItem') AS A(ProjectItem)
) as Data
where Data.ScriptCode is not null and ScriptType <> 'Other'

Sorry for the delay...
Is it something like this you are looking for?
(just paste it into an empty query window and adapt to your needs)
DECLARE #xml XML=
'<Root>
<ProjectItem Name="ScriptMain.cs" Encoding="UTF8">
<![CDATA[Here you find CS code]]>
</ProjectItem>
<ProjectItem Name="ScriptMain.vb" Encoding="UTF8">
<![CDATA[Here you find Vb code]]>
</ProjectItem>
<ProjectItem Name="OtherName.cs" Encoding="UTF8">
<![CDATA[Here you find CS code with other name]]>
</ProjectItem>
<ProjectItem Name="OtherName.vb" Encoding="UTF8">
<![CDATA[Here you find VB code with other name]]>
</ProjectItem>
<ProjectItem Name="OtherName.xy" Encoding="UTF8">
<![CDATA[Here you find XY code with other name]]>
</ProjectItem>
</Root>
'
;
select CASE WHEN ProjectItem.value('#Name','varchar(max)') LIKE '%.vb' THEN 'VB'
WHEN ProjectItem.value('#Name','varchar(max)') LIKE '%.cs' THEN 'CS'
ELSE 'Other'
END AS ScriptType
,ProjectItem.value('#Name','varchar(max)') AS ScriptName
,ProjectItem.value('.','varchar(max)') AS ScriptCode
FROM #xml.nodes('//ProjectItem') AS A(ProjectItem)
The Result is
ScriptType ScriptName ScriptCode
CS ScriptMain.cs Here you find CS code
VB ScriptMain.vb Here you find Vb code
CS OtherName.cs Here you find CS code with other name
VB OtherName.vb Here you find VB code with other name
Other OtherName.xy Here you find XY code with other name

Related

How to use coordinates from a sql table as parameter input in Weather URL API SSIS script?

I have created a SSIS-project that retrieves weather information (JSON format) using a Web API in a script task. I've been following this tutorial :Weather data API which works great if you only want to retrieve weather information from a fixed set of coordinates.
My goal now is to use a table where I have stored some coordinates as variable input in the API URL parameters instead of having the coordinates already set in the URL https://api.met.no/weatherapi/locationforecast/2.0/compact.json?lat=55.596&lon=15
So what I have done so for
Created a Script task that gathers the weather information:
#region Help: Introduction to the script task
/* The Script Task allows you to perform virtually any operation that can be accomplished in
* a .Net application within the context of an Integration Services control flow.
*
* Expand the other regions which have "Help" prefixes for examples of specific ways to use
* Integration Services features within this script task. */
#endregion
#region Namespaces
using System;
using System.Data;
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;
using System.Data.SqlClient;
using System.Net;
#endregion
namespace ST_6f60bececd8f4f94afaf758869590918
{
/// <summary>
/// ScriptMain is the entry point class of the script. Do not change the name, attributes,
/// or parent of this class.
/// </summary>
[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
#region Help: Using Integration Services variables and parameters in a script
/* To use a variable in this script, first ensure that the variable has been added to
* either the list contained in the ReadOnlyVariables property or the list contained in
* the ReadWriteVariables property of this script task, according to whether or not your
* code needs to write to the variable. To add the variable, save this script, close this instance of
* Visual Studio, and update the ReadOnlyVariables and
* ReadWriteVariables properties in the Script Transformation Editor window.
* To use a parameter in this script, follow the same steps. Parameters are always read-only.
*
* Example of reading from a variable:
* DateTime startTime = (DateTime) Dts.Variables["System::StartTime"].Value;
*
* Example of writing to a variable:
* Dts.Variables["User::myStringVariable"].Value = "new value";
*
* Example of reading from a package parameter:
* int batchId = (int) Dts.Variables["$Package::batchId"].Value;
*
* Example of reading from a project parameter:
* int batchId = (int) Dts.Variables["$Project::batchId"].Value;
*
* Example of reading from a sensitive project parameter:
* int batchId = (int) Dts.Variables["$Project::batchId"].GetSensitiveValue();
* */
#endregion
#region Help: Firing Integration Services events from a script
/* This script task can fire events for logging purposes.
*
* Example of firing an error event:
* Dts.Events.FireError(18, "Process Values", "Bad value", "", 0);
*
* Example of firing an information event:
* Dts.Events.FireInformation(3, "Process Values", "Processing has started", "", 0, ref fireAgain)
*
* Example of firing a warning event:
* Dts.Events.FireWarning(14, "Process Values", "No values received for input", "", 0);
* */
#endregion
#region Help: Using Integration Services connection managers in a script
/* Some types of connection managers can be used in this script task. See the topic
* "Working with Connection Managers Programatically" for details.
*
* Example of using an ADO.Net connection manager:
* object rawConnection = Dts.Connections["Sales DB"].AcquireConnection(Dts.Transaction);
* SqlConnection myADONETConnection = (SqlConnection)rawConnection;
* //Use the connection in some code here, then release the connection
* Dts.Connections["Sales DB"].ReleaseConnection(rawConnection);
*
* Example of using a File connection manager
* object rawConnection = Dts.Connections["Prices.zip"].AcquireConnection(Dts.Transaction);
* string filePath = (string)rawConnection;
* //Use the connection in some code here, then release the connection
* Dts.Connections["Prices.zip"].ReleaseConnection(rawConnection);
* */
#endregion
/// <summary>
/// This method is called when this script task executes in the control flow.
/// Before returning from this method, set the value of Dts.TaskResult to indicate success or failure.
/// To open Help, press F1.
/// </summary>
public void Main()
{
string Longitude = (string)Dts.Variables["User::Longitude"].Value.ToString();
string Latitude = (string)Dts.Variables["User::Latitude"].Value.ToString();
var url = #"https://api.met.no/weatherapi/locationforecast/2.0/compact.json?lat=55.596&lon=15";
System.Net.ServicePointManager.DefaultConnectionLimit = int.MaxValue;
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.UseDefaultCredentials = true;
req.Proxy.Credentials = System.Net.CredentialCache.DefaultCredentials;
var syncClient = new WebClient();
syncClient.Headers.Add("user-agent", "acmeweathersite.com support#acmeweathersite.com");
var content = syncClient.DownloadString(url);
string connectionString = "Data Source=localhost;Initial Catalog=Weather;Integrated Security=True;";
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand Storproc =
new SqlCommand(#"INSERT INTO [dbo].[Weather] (JSONData)
select #JSONData", conn);
Storproc.Parameters.AddWithValue("#JSONData", content.ToString());
conn.Open();
Storproc.ExecuteNonQuery();
conn.Close();
}
// TODO: Add your code here
Dts.TaskResult = (int)ScriptResults.Success;
}
#region ScriptResults declaration
/// <summary>
/// This enum provides a convenient shorthand within the scope of this class for setting the
/// result of the script.
///
/// This code was generated automatically.
/// </summary>
enum ScriptResults
{
Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
};
#endregion
}
}
Created a SQL table with coordinates:
create table Coordinates(
Municipality nvarchar(50),
Latitide nvarchar(50),
Longitude nvarchar(50)
)
INSERT INTO Coordinates (Municipality, Latitide, Longitude)
VALUES (114, 59.5166667, 17.9),
(115, 59.5833333, 18.2),
(117, 59.5, 18.45)
Added the coordinates table as an SQL task:
Finally I added the two variables in the Script task code:
string Longitude = (string)Dts.Variables["User::Longitude"].Value.ToString();
string Latitude = (string)Dts.Variables["User::Latitude"].Value.ToString();
var url = #"https://api.met.no/weatherapi/locationforecast/2.0/compact.json?lat=Latitude&lon=Longitude";
But when I execute the package the Foreach Loop Container just loops forever, no errors pops up but no data is being stored in the database table either. It feels like I have missed something but no really sure what. Very novice when it comes to variables in SSIS so excuse my lack of knowledge. in my example I am using a SQL Server 2019.
Looks like you are nearly there.
Few things to check:
In your SQL, you are selecting 3 columns, so, the 'index' for these columns would be:
Municipality -> index = 0
Latitide -> index = 1
Longitude -> index = 2
i.e., in the variable mapping, you need to use index 1 and 2 instead of zero for all.
https://learn.microsoft.com/en-us/sql/integration-services/control-flow/foreach-loop-container?view=sql-server-ver15
The first column defined in the enumerator item has the index value 0, the second column 1, and so on.
Your spelling for the columns also seems different (Latitide Vs Latitude). Cross-check this as well. i.e., if you run your sql statement manually are you able to see the data ? What are the column names for the result ?
You can also check the variables in your script task (for debugging purposes) by adding a MessageBox.
E.g.,
string longitude = (string)Dts.Variables["User::Longitude"].Value.ToString();
string latitide = (string)Dts.Variables["User::Latitide"].Value.ToString();
string municipality = (string)Dts.Variables["User::Municipality"].Value.ToString();
MessageBox.Show("longitude:" + longitude + ", latitide:" + latitide + ", municipality: " + municipality);

An Issue with automatic code documentation | PHP

I'm having a CodeIgniter project with lots of modules and codes residing in it.
Now I would like to create documentation for other new co-workers.
For example
/**
* inline tags demonstration
*
* This class generates bars using the main algorithm, which also
* works heavily with {#link foo()} to rule the world. If I want
* to use the characters "{#link" in a docblock, I just use "{#}link." If
* I want the characters "{#*}" I use "{#}*}"
*
* #author ahobbit
* #copyright middleearth.org XVII
* #version 1.2.3
*/
class bar
{
// beginning of docblock template area
/**##+
* #access private
* #var string
*/
var $_var1 = 'hello';
var $_var2 = 'my';
var $_var3 = 'name';
var $_var4 = 'is';
var $_var5 = 'Bob';
var $_var6 = 'and';
var $_var7 = 'I';
/**
* Two words
*/
var $_var8 = 'like strings';
/**##-*/
var $publicvar = 'Lookee me!';
}
/**
* Makes chocolate bars
*
* There are two aspects to this class.
* {#inheritdoc } In addition, the foo class
* makes the bars chocolate
*/
class foo extends bar
{
/**
* Check if a Sql row exists. (with two values)
*
* This function will check if a selected sql row exists that contains two
* known values.
*
* #param string $tblname Sql Table Name
* #param string $colname Sql Column Name 1
* #param string $value Sql value 1
* #param string $colname2 Sql Column Name 2
* #param string $value2 Sql value 2
* #return boolean returns true if the sql row does exist
*/
function tableHasRow2D($tblname, $colname, $value, $colname2, $value2) {
$row = sqlQuery("SELECT COUNT(*) AS count FROM $tblname WHERE " . "$colname
LIKE '$value' AND $colname2 LIKE '$value2'");
return $row['count'] ? true : false;
}
}
I had made use of with the following commands
I. PHPdoc
E:\xampp\htdocs\CI_Proj> php phpDocumentor.phar -d application
PHP Warning: count(): Parameter must be an array or an object that implements Countable in phar://E:/xampp/htdocs/CI_Proj/phpDocumentor.phar/vendor/twig/twig/lib/Twig/Extension/Core.php on line 1293
Execute transformation using writer "twig"
Execute transformation using writer "twig"
Execute transformation using writer "twig"
Execute transformation using writer "twig"
Execute transformation using writer "twig"
Execute transformation using writer "Graph"
Unable to find the `dot` command of the GraphViz package. Is GraphViz correctly installed and present in your path? 222.041s
Analyze results and write a report to log
.. 0.004s
II. ApiGen
E:\xampp\htdocs\CI_Proj\vendor\bin>apigen generate src "E:\xampp\htdocs\CI_Proj\application" --destination "E:\xampp\htdocs\CI_Proj\docs-apigen" --debug
Fatal error: Cannot use 'Object' as class name as it is reserved in E:\xampp\htdocs\CI_Proj\vendor\latte\latte\src\Latte\Object.php on line 14
ErrorException: Cannot use 'Object' as class name as it is reserved in E:\xampp\htdocs\CI_Proj\vendor\latte\latte\src\Latte\Object.php:14
Stack trace:
#0 [internal function]: Tracy\Debugger::shutdownHandler()
#1 {main}
Unable to log error: Logging directory is not specified.
Is it be possible to add comments to the modules before or after generation of code?
Since I'm looking to add comments to specific modules in the document itself and optionally that would
automatically inserted to the source code, at the right positions.
How should I need to manage the modules and code inside the project and How would I create a browsable HTML-Documentation?
Any other tool that makes generating the documentation directly from your PHP source code, that would be great in advice

Is it possible to export the outcome of SqlString-edited strings?

I have an installer which installs a database. The database is created alongside some logins. To create the logins I am using the master database in SqlString elements. Access to the master database is only granted to users who have very high privileges on the SQL server. Oftentimes the installation is aborted because a SQL string designated for the master database cannot be executed due the lack of rights.
I want to edit my installer, so that when a SqlString element cannot be executed, the SQL part of the installation shall be skipped. After the installation has taken place I want the user to be able to execute the SQL statements herself. Every SQL action taken by my installer is stored in SqlString elements. The SqlString elements contain a lot of properties which get replaced during the installation. I want to extract the content of all edited SqlString elements into one sql file stored in the user directory.
I guess I'll have to write a customaction which takes place after the sqlextension has substituted the properties. And then I'll have to access these altered strings. Is there any way I can do this?
Example SqlString element:
<sql:SqlDatabase Id="MasterDB" Server="[SQLSERVER_SERVER]" Instance="[SQLSERVER_INSTANCENAME]" Database="master" />
<sql:SqlString
SqlDb="MasterDB"
Id="CreateNetworkServiceAccount"
ExecuteOnInstall="yes"
ContinueOnError="no"
SQL="IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = N'{[WIX_ACCOUNT_NETWORKSERVICE]}')
CREATE LOGIN [\[]{[WIX_ACCOUNT_NETWORKSERVICE]}[\]] FROM WINDOWS WITH DEFAULT_DATABASE=[\[]master[\]]"
Sequence="101"/>
Example of the sql file I'd like to have after the SqlStrings have failed:
USE master;
IF NOT EXISTS (SELECT * FROM sys.server_principals WHERE name = N'NT AUTHORITY\Network Service')
CREATE LOGIN [NT AUTHORITY\Network Service] FROM WINDOWS WITH DEFAULT_DATABASE=[master]
I have solved this problem with a rather odd solution. I have written a CustomAction which extracts the String elements from the SqlString table and then replaces the formatted fields with the appropriate Properties stored in the session. To have access to the session variable, the CustomAction has to be executed as immediate. I've scheduled it before InstallFinalize to be given access to the PersonalFolder property. With this property I am able to store a Sql script generated by the entries in the SqlScript table in the users Documents directory. To account for different databases in the Installation, I have included a lookup in the SqlDatabase table.
Here is the code to the CustomAction:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Deployment.WindowsInstaller;
using System.IO;
using System.Text.RegularExpressions;
namespace SaveSqlStrings
{
public class CustomActions
{
[CustomAction]
public static ActionResult SaveSqlStrings(Session session)
{
StringBuilder sqlStrings = new StringBuilder();
Database db = session.Database;
View view = db.OpenView("SELECT * FROM `SqlString`");
IList<string> SqlStringElements = db.ExecuteStringQuery("SELECT `String` FROM `SqlString`");
Regex bracketedProperties = new Regex(#"\[(\b[A-Z_]*\b)\]");
Regex formattedProperties = new Regex(#"{\[(\b[A-Z_]*\b)\]}");
Regex openeningSquareBrackets = new Regex(#"\[\\\[\]");
Regex closingSquareBrackets = new Regex(#"\[\\\]\]");
string sqlDb_ = "";
string sqlString = "";
string Database = "";
foreach (string dbString in SqlStringElements)
{
sqlDb_ = (string)db.ExecuteScalar("SELECT `SqlDb_` FROM `SqlString` WHERE `String` ='{0}'",dbString);
sqlString = (string)db.ExecuteScalar("SELECT `SQL` FROM `SqlString` WHERE `String` ='{0}'",dbString);
view.Close();
view = db.OpenView("SELECT * FROM `SqlDatabase`");
Database = (string)db.ExecuteScalar("SELECT `Database` from `SqlDatabase` WHERE `SqlDb`='{0}'", sqlDb_);
if(bracketedProperties.IsMatch(Database))
{
Database = bracketedProperties.Match(Database).Groups[1].Value;
Database = session[Database];
}
if (openeningSquareBrackets.IsMatch(sqlString))
sqlString = openeningSquareBrackets.Replace(sqlString, "[");
if (closingSquareBrackets.IsMatch(sqlString))
sqlString = closingSquareBrackets.Replace(sqlString, "]");
if(formattedProperties.IsMatch(sqlString))
{
string propertyName = formattedProperties.Match(sqlString).Groups[1].Value;
string propertyValue = session[propertyName];
sqlString = formattedProperties.Replace(sqlString, propertyValue);
}
sqlStrings.AppendLine(String.Format("use {0}",Database));
sqlStrings.AppendLine(sqlString);
}
string home = session["PersonalFolder"];
string sqlPath = string.Concat(home, #"Script.sql");
try
{
File.WriteAllText(sqlPath, sqlStrings.ToString());
}
catch (Exception ex)
{
session["FailedTowrite"] = sqlPath;
}
view.Close();
db.Close();
return ActionResult.Success;
}
}
}

To have or not to have enums [duplicate]

How do I automatically create an enum and subsequently use its values in C# based on values in a database lookup table (using enterprise library data layer)?
For example, If I add a new lookup value in the database, I don't want to have to manually add the extra static enum value declaration in code - I'd like to keep the enum in sync with the database.
Is there such a thing as this?
I don't want to create a code generated static enum (as per The Code Project article Enum Code Generator - Generating enum code automatically from database look up tables) and would prefer it to be completely automatic.
I'm doing this exact thing, but you need to do some kind of code generation for this to work.
In my solution, I added a project "EnumeratedTypes". This is a console application which gets all of the values from the database and constructs the enums from them. Then it saves all of the enums to an assembly.
The enum generation code is like this:
// Get the current application domain for the current thread
AppDomain currentDomain = AppDomain.CurrentDomain;
// Create a dynamic assembly in the current application domain,
// and allow it to be executed and saved to disk.
AssemblyName name = new AssemblyName("MyEnums");
AssemblyBuilder assemblyBuilder = currentDomain.DefineDynamicAssembly(name,
AssemblyBuilderAccess.RunAndSave);
// Define a dynamic module in "MyEnums" assembly.
// For a single-module assembly, the module has the same name as the assembly.
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(name.Name,
name.Name + ".dll");
// Define a public enumeration with the name "MyEnum" and an underlying type of Integer.
EnumBuilder myEnum = moduleBuilder.DefineEnum("EnumeratedTypes.MyEnum",
TypeAttributes.Public, typeof(int));
// Get data from database
MyDataAdapter someAdapter = new MyDataAdapter();
MyDataSet.MyDataTable myData = myDataAdapter.GetMyData();
foreach (MyDataSet.MyDataRow row in myData.Rows)
{
myEnum.DefineLiteral(row.Name, row.Key);
}
// Create the enum
myEnum.CreateType();
// Finally, save the assembly
assemblyBuilder.Save(name.Name + ".dll");
My other projects in the solution reference this generated assembly. As a result, I can then use the dynamic enums in code, complete with intellisense.
Then, I added a post-build event so that after this "EnumeratedTypes" project is built, it runs itself and generates the "MyEnums.dll" file.
By the way, it helps to change the build order of your project so that "EnumeratedTypes" is built first. Otherwise, once you start using your dynamically generated .dll, you won't be able to do a build if the .dll ever gets deleted. (Chicken and egg kind of problem -- your other projects in the solution need this .dll to build properly, and you can't create the .dll until you build your solution...)
I got most of the above code from this msdn article.
Enums must be specified at compile time, you can't dynamically add enums during run-time - and why would you, there would be no use/reference to them in the code?
From Professional C# 2008:
The real power of enums in C# is that behind the scenes they are instantiated as structs derived from the base class, System.Enum . This means it is possible to call methods against them to perform some useful tasks. Note that because of the way the .NET Framework is implemented there is no performance loss associated with treating the enums syntactically as structs. In practice, once your code is compiled, enums will exist as primitive types, just like int and float .
So, I'm not sure you can use Enums the way you want to.
Does it have to be an actual enum? How about using a Dictionary<string,int> instead?
for example
Dictionary<string, int> MyEnum = new Dictionary(){{"One", 1}, {"Two", 2}};
Console.WriteLine(MyEnum["One"]);
I've done this with a T4 template. It is fairly trivial to drop a .tt file into your project, and set up Visual Studio to run the T4 template as a pre-build step.
The T4 generates a .cs file, which means you can have it just query the database and build an enum in a .cs file from the result. Wired up as a pre-build task, it would re-create your enum on every build, or you can run the T4 manually as needed instead.
Let's say you have the following in your DB:
table enums
-----------------
| id | name |
-----------------
| 0 | MyEnum |
| 1 | YourEnum |
-----------------
table enum_values
----------------------------------
| id | enums_id | value | key |
----------------------------------
| 0 | 0 | 0 | Apple |
| 1 | 0 | 1 | Banana |
| 2 | 0 | 2 | Pear |
| 3 | 0 | 3 | Cherry |
| 4 | 1 | 0 | Red |
| 5 | 1 | 1 | Green |
| 6 | 1 | 2 | Yellow |
----------------------------------
Construct a select to get the values you need:
select * from enums e inner join enum_values ev on ev.enums_id=e.id where e.id=0
Construct the source code for the enum and you'll get something like:
String enumSourceCode = "enum " + enumName + "{" + enumKey1 + "=" enumValue1 + "," + enumKey2 + ... + "}";
(obviously this is constructed in a loop of some kind.)
Then comes the fun part, Compiling your enum and using it:
CodeDomProvider provider = CodeDomProvider.CreateProvider("CSharp");
CompilerParameters cs = new CompilerParameters();
cp.GenerateInMemory = True;
CompilerResult result = provider.CompileAssemblyFromSource(cp, enumSourceCode);
Type enumType = result.CompiledAssembly.GetType(enumName);
Now you have the type compiled and ready for use.
To get a enum value stored in the DB you can use:
[Enum].Parse(enumType, value);
where value can be either the integer value (0, 1, etc.) or the enum text/key (Apple, Banana, etc.)
Just showing the answer of Pandincus with "of the shelf" code and some explanation:
You need two solutions for this example ( I know it could be done via one also ; ), let the advanced students present it ...
So here is the DDL SQL for the table :
USE [ocms_dev]
GO
CREATE TABLE [dbo].[Role](
[RoleId] [int] IDENTITY(1,1) NOT NULL,
[RoleName] [varchar](50) NULL
) ON [PRIMARY]
So here is the console program producing the dll:
using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;
using System.Data.Common;
using System.Data;
using System.Data.SqlClient;
namespace DynamicEnums
{
class EnumCreator
{
// after running for first time rename this method to Main1
static void Main ()
{
string strAssemblyName = "MyEnums";
bool flagFileExists = System.IO.File.Exists (
AppDomain.CurrentDomain.SetupInformation.ApplicationBase +
strAssemblyName + ".dll"
);
// Get the current application domain for the current thread
AppDomain currentDomain = AppDomain.CurrentDomain;
// Create a dynamic assembly in the current application domain,
// and allow it to be executed and saved to disk.
AssemblyName name = new AssemblyName ( strAssemblyName );
AssemblyBuilder assemblyBuilder =
currentDomain.DefineDynamicAssembly ( name,
AssemblyBuilderAccess.RunAndSave );
// Define a dynamic module in "MyEnums" assembly.
// For a single-module assembly, the module has the same name as
// the assembly.
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule (
name.Name, name.Name + ".dll" );
// Define a public enumeration with the name "MyEnum" and
// an underlying type of Integer.
EnumBuilder myEnum = moduleBuilder.DefineEnum (
"EnumeratedTypes.MyEnum",
TypeAttributes.Public,
typeof ( int )
);
#region GetTheDataFromTheDatabase
DataTable tableData = new DataTable ( "enumSourceDataTable" );
string connectionString = "Integrated Security=SSPI;Persist " +
"Security Info=False;Initial Catalog=ocms_dev;Data " +
"Source=ysg";
using (SqlConnection connection =
new SqlConnection ( connectionString ))
{
SqlCommand command = connection.CreateCommand ();
command.CommandText = string.Format ( "SELECT [RoleId], " +
"[RoleName] FROM [ocms_dev].[dbo].[Role]" );
Console.WriteLine ( "command.CommandText is " +
command.CommandText );
connection.Open ();
tableData.Load ( command.ExecuteReader (
CommandBehavior.CloseConnection
) );
} //eof using
foreach (DataRow dr in tableData.Rows)
{
myEnum.DefineLiteral ( dr[1].ToString (),
Convert.ToInt32 ( dr[0].ToString () ) );
}
#endregion GetTheDataFromTheDatabase
// Create the enum
myEnum.CreateType ();
// Finally, save the assembly
assemblyBuilder.Save ( name.Name + ".dll" );
} //eof Main
} //eof Program
} //eof namespace
Here is the Console programming printing the output ( remember that it has to reference the dll ). Let the advance students present the solution for combining everything in one solution with dynamic loading and checking if there is already build dll.
// add the reference to the newly generated dll
use MyEnums ;
class Program
{
static void Main ()
{
Array values = Enum.GetValues ( typeof ( EnumeratedTypes.MyEnum ) );
foreach (EnumeratedTypes.MyEnum val in values)
{
Console.WriteLine ( String.Format ( "{0}: {1}",
Enum.GetName ( typeof ( EnumeratedTypes.MyEnum ), val ),
val ) );
}
Console.WriteLine ( "Hit enter to exit " );
Console.ReadLine ();
} //eof Main
} //eof Program
Aren't we coming to this from the wrong direction?
If the data is likely to change at all during the lifetime of the deployed release then an enum is just not appropriate, and you need to use a dictionary, hash or other dynamic collection.
If you know the set of possible values is fixed for the life of the deployed release, then an enum is preferable.
If you must have something in your database that replicates the enumerated set, then why not add a deployment step to clear and repopulate the database table with the definitive set of enum values?
I always like to write my own "custom enum". Than I have one class that is a little bit more complex, but I can reuse it:
public abstract class CustomEnum
{
private readonly string _name;
private readonly object _id;
protected CustomEnum( string name, object id )
{
_name = name;
_id = id;
}
public string Name
{
get { return _name; }
}
public object Id
{
get { return _id; }
}
public override string ToString()
{
return _name;
}
}
public abstract class CustomEnum<TEnumType, TIdType> : CustomEnum
where TEnumType : CustomEnum<TEnumType, TIdType>
{
protected CustomEnum( string name, TIdType id )
: base( name, id )
{ }
public new TIdType Id
{
get { return (TIdType)base.Id; }
}
public static TEnumType FromName( string name )
{
try
{
return FromDelegate( entry => entry.Name.Equals( name ) );
}
catch (ArgumentException ae)
{
throw new ArgumentException( "Illegal name for custom enum '" + typeof( TEnumType ).Name + "'", ae );
}
}
public static TEnumType FromId( TIdType id )
{
try
{
return FromDelegate( entry => entry.Id.Equals( id ) );
}
catch (ArgumentException ae)
{
throw new ArgumentException( "Illegal id for custom enum '" + typeof( TEnumType ).Name + "'", ae );
}
}
public static IEnumerable<TEnumType> GetAll()
{
var elements = new Collection<TEnumType>();
var infoArray = typeof( TEnumType ).GetFields( BindingFlags.Public | BindingFlags.Static );
foreach (var info in infoArray)
{
var type = info.GetValue( null ) as TEnumType;
elements.Add( type );
}
return elements;
}
protected static TEnumType FromDelegate( Predicate<TEnumType> predicate )
{
if(predicate == null)
throw new ArgumentNullException( "predicate" );
foreach (var entry in GetAll())
{
if (predicate( entry ))
return entry;
}
throw new ArgumentException( "Element not found while using predicate" );
}
}
Now I just need to create my enum I want to use:
public sealed class SampleEnum : CustomEnum<SampleEnum, int>
{
public static readonly SampleEnum Element1 = new SampleEnum( "Element1", 1, "foo" );
public static readonly SampleEnum Element2 = new SampleEnum( "Element2", 2, "bar" );
private SampleEnum( string name, int id, string additionalText )
: base( name, id )
{
AdditionalText = additionalText;
}
public string AdditionalText { get; private set; }
}
At last I can use it like I want:
static void Main( string[] args )
{
foreach (var element in SampleEnum.GetAll())
{
Console.WriteLine( "{0}: {1}", element, element.AdditionalText );
Console.WriteLine( "Is 'Element2': {0}", element == SampleEnum.Element2 );
Console.WriteLine();
}
Console.ReadKey();
}
And my output would be:
Element1: foo
Is 'Element2': False
Element2: bar
Is 'Element2': True
You want System.Web.Compilation.BuildProvider
I also doubt the wisdom of doing this, but then there maybe a good use case that I can't think of.
What you're looking for are Build Providers i.e. System.Web.Compilation.BuildProvider
They're used very effectively by SubSonic, you can download the source and see how they use them, you won't need anything half as intricate as what they're doing.
Hope this helps.
Using dynamic enums is bad no matter which way. You will have to go through the trouble of "duplicating" the data to ensure clear and easy code easy to maintain in the future.
If you start introducing automatic generated libraries, you are for sure causing more confusion to future developers having to upgrade your code than simply making your enum coded within the appropriate class object.
The other examples given sound nice and exciting, but think about the overhead on code maintenance versus what you get from it. Also, are those values going to change that frequently?
Word up, I as well got tired of writing out enumerations based on Id / Name db table columns, copying and pasting stuff from queries in SSMS.
Below is a super dirty stored procedure that takes as input a table name, the column name you want to use for the c# enumeration name, and the column name that you want to use for the c# enumeration value.
Most of theses table names I work with a) end with "s" b) have a [TABLENAME]Id column and c) have a [TABLENAME]Name column, so there are a couple if statements that will assume that structure, in which case, the column name parameters are not required.
A little context for these examples - "Stonk" here doesn't really mean "stock" but kinda, the way I'm using "stonk" it means "a thing that has some numbers associated to it for a time period" But that's not important, it's just an example of table with this Id / Name schema. It looks like this:
CREATE TABLE StonkTypes (
StonkTypeId TINYINT IDENTITY(1,1) PRIMARY KEY NOT NULL,
StonkTypeName VARCHAR(200) NOT NULL CONSTRAINT UQ_StonkTypes_StonkTypeName UNIQUE (StonkTypeName)
)
After I create the proc, this statement:
EXEC CreateCSharpEnum 'StonkTypes'
Selects this string:
public enum StonkTypes { Stonk = 1, Bond = 2, Index = 3, Fund = 4, Commodity = 5,
PutCallRatio = 6, }
Which I can copy and paste into a C# file.
I have a Stonks table and it has StonkId and StonkName columns so this exec:
EXEC CreateCSharpEnum 'Stonks'
Spits out:
public enum Stonks { SP500 = 1, DowJonesIndustrialAverage = 2, ..... }
But for that enum I want to use the "Symbol" column for the enum name values so this:
EXEC CreateCSharpEnum 'Stonks', 'Symbol'
Does the trick and renders:
public enum Stonks { SPY = 1, DIA = 2, ..... }
Without further ado, here is this dirty piece of craziness. Yeah, very dirty, but I'm kind of pleased with myself - it's SQL code that constructs SQL code that constructs C# code. Couple layers involved.
CREATE OR ALTER PROCEDURE CreateCSharpEnum
#TableName VARCHAR(MAX),
#EnumNameColumnName VARCHAR(MAX) = NULL,
#EnumValueColumnName VARCHAR(MAX) = NULL
AS
DECLARE #LastCharOfTableName VARCHAR(1)
SELECT #LastCharOfTableName = RIGHT(#TableName, 1)
PRINT 'Last char = [' + #LastCharOfTableName + ']'
DECLARE #TableNameWithoutS VARCHAR(MAX)
IF UPPER(#LastCharOfTableName) = 'S'
SET #TableNameWithoutS = LEFT(#TableName, LEN(#TableName) - 1)
ELSE
SET #TableNameWithoutS = #TableName
PRINT 'Table name without trailing s = [' + #TableNameWithoutS + ']'
IF #EnumNameColumnName IS NULL
BEGIN
SET #EnumNameColumnName = #TableNameWithoutS + 'Name'
END
PRINT 'name col name = [' + #EnumNameColumnName + ']'
IF #EnumValueColumnName IS NULL
SET #EnumValueColumnName = #TableNameWithoutS + 'Id'
PRINT 'value col name = [' + #EnumValueColumnName + ']'
-- replace spaces and punctuation
SET #EnumNameColumnName = 'REPLACE(' + #EnumNameColumnName + ', '' '', '''')'
SET #EnumNameColumnName = 'REPLACE(' + #EnumNameColumnName + ', ''&'', '''')'
SET #EnumNameColumnName = 'REPLACE(' + #EnumNameColumnName + ', ''.'', '''')'
SET #EnumNameColumnName = 'REPLACE(' + #EnumNameColumnName + ', ''('', '''')'
SET #EnumNameColumnName = 'REPLACE(' + #EnumNameColumnName + ', '')'', '''')'
PRINT 'name col name with replace sql = [' + #EnumNameColumnName + ']'
DECLARE #SqlStr VARCHAR(MAX) = 'SELECT ' + #EnumNameColumnName
+ ' + '' = '''
+ ' + LTRIM(RTRIM(STR(' + #EnumValueColumnName + '))) + '','' FROM ' + #TableName + ' ORDER BY ' + #EnumValueColumnName
PRINT 'sql that gets rows for enum body = [' + #SqlStr + ']'
CREATE TABLE #EnumRowsTemp (s VARCHAR(MAX))
INSERT
INTO #EnumRowsTemp
EXEC(#SqlStr)
--SELECT * FROM #EnumRowsTemp
DECLARE #csharpenumbody VARCHAR(MAX)
SELECT #csharpenumbody = COALESCE(#csharpenumbody + ' ', '') + s FROM #EnumRowsTemp
--PRINT #csharpenumbody
DECLARE #csharpenum VARCHAR(MAX) = 'public enum ' + #TableName + ' { ' + #csharpenumbody + ' }'
PRINT #csharpenum
SELECT #csharpenum
DROP TABLE #EnumRowsTemp
Please, be critical. One funky thing I didn't understand, how come I have to create and drop this #EnumRowsTemp table and not just "SELECT INTO #EnumRowsTemp" to create the temp table on the fly? I don't know the answer, I tried that and it didn't work. That's probably the least of the problems of this code...
As dirty as it may be... I hope this saves some of you fellow dorks a little bit of time.
I don't think there is a good way of doing what you want. And if you think about it I don't think this is what you really want.
If you would have a dynamic enum, it also means you have to feed it with a dynamic value when you reference it. Maybe with a lot of magic you could achieve some sort of IntelliSense that would take care of this and generate an enum for you in a DLL file. But consider the amount of work it would take, how uneffective it would be to access the database to fetch IntelliSense information as well as the nightmare of version controlling the generated DLL file.
If you really don't want to manually add the enum values (you'll have to add them to the database anyway) use a code generation tool instead, for example T4 templates. Right click+run and you got your enum statically defined in code and you get all the benefits of using enums.
One way to keep the Enums and to create a Dynamic list of values at the same time is to use the Enums that you currently have with a Dynamically created Dictionary.
Since most Enums are used in the context that they are defined to be used, and the "dynamic enums" will be supported by dynamic processes, you can distinguish the 2.
The first step is to create a table/collection that houses the IDs and References for the Dynamic Entries. In the table you will autoincrement much larger than your largest Enum value.
Now comes the part for your dynamic Enums, I am assuming that you will be using the Enums to create a set of conditions that apply a set of rules, some are dynamically generated.
Get integer from database
If Integer is in Enum -> create Enum -> then run Enum parts
If Integer is not a Enum -> create Dictionary from Table -> then run Dictionary parts.
enum builder class
public class XEnum
{
private EnumBuilder enumBuilder;
private int index;
private AssemblyBuilder _ab;
private AssemblyName _name;
public XEnum(string enumname)
{
AppDomain currentDomain = AppDomain.CurrentDomain;
_name = new AssemblyName("MyAssembly");
_ab = currentDomain.DefineDynamicAssembly(
_name, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder mb = _ab.DefineDynamicModule("MyModule");
enumBuilder = mb.DefineEnum(enumname, TypeAttributes.Public, typeof(int));
}
/// <summary>
/// adding one string to enum
/// </summary>
/// <param name="s"></param>
/// <returns></returns>
public FieldBuilder add(string s)
{
FieldBuilder f = enumBuilder.DefineLiteral(s, index);
index++;
return f;
}
/// <summary>
/// adding array to enum
/// </summary>
/// <param name="s"></param>
public void addRange(string[] s)
{
for (int i = 0; i < s.Length; i++)
{
enumBuilder.DefineLiteral(s[i], i);
}
}
/// <summary>
/// getting index 0
/// </summary>
/// <returns></returns>
public object getEnum()
{
Type finished = enumBuilder.CreateType();
_ab.Save(_name.Name + ".dll");
Object o1 = Enum.Parse(finished, "0");
return o1;
}
/// <summary>
/// getting with index
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
public object getEnum(int i)
{
Type finished = enumBuilder.CreateType();
_ab.Save(_name.Name + ".dll");
Object o1 = Enum.Parse(finished, i.ToString());
return o1;
}
}
create an object
string[] types = { "String", "Boolean", "Int32", "Enum", "Point", "Thickness", "long", "float" };
XEnum xe = new XEnum("Enum");
xe.addRange(types);
return xe.getEnum();
You could use CodeSmith to generate something like this:
http://www.csharping.com/PermaLink,guid,cef1b637-7d37-4691-8e49-138cbf1d51e9.aspx

How to calculate square root in sqlite

I need to calculate an euclidean distance in a sqlite database.
Does anyone know how to calculate square roots in sqlite beside writing and loading a dynamic library for math functions?
I am close to resorting to the fast inverse square root algorithm in here http://en.wikipedia.org/wiki/Fast_inverse_square_root though it might to turn into more fun than I need right now.
And as a side note, it'd be great to figure out how to do power (which is the generalized question, and is cleaner coding than multiplying a number by itself).
Thanks,
Simone
Well, I have a semi-answer.
Yes it involves a 3rd party, but you don't have to write it yourself : did you check the last extension on this page ?
It includes several math functions, and amongst them is sqrt().
Warning: this answer is dependent on the coding language. In my case C#.
User defined SQLite functions was for me a pain to implement. Finally, after a long time of searching I was able to implement it in my C# code. Main function looks like this:
[SQLiteFunction(Arguments = 1, FuncType = FunctionType.Scalar, Name = "Sqrt")]
class Sqrt : SQLiteFunction
{
public override object Invoke(object[] args)
{
return Math.Sqrt(Double.Parse(args[0].ToString()));
}
}
Registration of custom function:
SQLiteFunction.RegisterFunction(typeof(Sqrt));
And using in select:
SQLiteCommand com = new SQLiteCommand("select sqrt(10.42)", connection);
You can download full example here: http://db.tt/qzeNXwso
Or, if you want only view code (or get through all parts of my code), I paste below full working example code for calculate square root in SQLite database, because is very hard to find any working code for this. To create and run this example do this 6 steps:
Create new project (my name is Sqrt)
Include SQLite reference to your project: Solution Explorer -> References (right click: Add reference) -> Assemblies - Extensions - System.Data.SQLite (check) -> OK
Open App.config and replace to this (without this step you maybe get Mixed mode assembly error):
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup useLegacyV2RuntimeActivationPolicy="true">
<supportedRuntime version="v4.0"/>
</startup>
</configuration>
Replace your Form1.Designer.cs with this code:
namespace Sqrt
{
partial class Form1
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.txb_Input = new System.Windows.Forms.TextBox();
this.txb_Output = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.btn_Calcualte = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// txb_Input
//
this.txb_Input.Location = new System.Drawing.Point(131, 12);
this.txb_Input.Name = "txb_Input";
this.txb_Input.Size = new System.Drawing.Size(201, 20);
this.txb_Input.TabIndex = 0;
//
// txb_Output
//
this.txb_Output.BackColor = System.Drawing.Color.WhiteSmoke;
this.txb_Output.Location = new System.Drawing.Point(131, 38);
this.txb_Output.Name = "txb_Output";
this.txb_Output.ReadOnly = true;
this.txb_Output.Size = new System.Drawing.Size(201, 20);
this.txb_Output.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 15);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(31, 13);
this.label1.TabIndex = 1;
this.label1.Text = "Input";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(12, 41);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(39, 13);
this.label2.TabIndex = 1;
this.label2.Text = "Output";
//
// btn_Calcualte
//
this.btn_Calcualte.Location = new System.Drawing.Point(257, 64);
this.btn_Calcualte.Name = "btn_Calcualte";
this.btn_Calcualte.Size = new System.Drawing.Size(75, 23);
this.btn_Calcualte.TabIndex = 2;
this.btn_Calcualte.Text = "Calculate";
this.btn_Calcualte.UseVisualStyleBackColor = true;
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(344, 98);
this.Controls.Add(this.btn_Calcualte);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this.txb_Output);
this.Controls.Add(this.txb_Input);
this.Name = "Form1";
this.Text = "Root square example";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox txb_Input;
private System.Windows.Forms.TextBox txb_Output;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Button btn_Calcualte;
}
}
Open Form1.cs (code) and replace code with this:
using System;
using System.Data.SQLite;
using System.Windows.Forms;
namespace Sqrt
{
// definition of custom sqlite function
[SQLiteFunction(Arguments = 1, FuncType = FunctionType.Scalar, Name = "Sqrt")]
class Sqrt : SQLiteFunction
{
public override object Invoke(object[] args)
{
return Math.Sqrt(Double.Parse(args[0].ToString())); // return result of math sqrt function
}
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.btn_Calcualte.Click += new System.EventHandler(this.btn_Calcualte_Click);
}
private void btn_Calcualte_Click(object sender, EventArgs e)
{
if (txb_Input.Text.Length == 0)
return;
try { SQLiteConnection.CreateFile(AppDomain.CurrentDomain.BaseDirectory + "test.s3db"); }
catch { }
SQLiteConnection con = new SQLiteConnection("Data Source=test.s3db");
SQLiteFunction.RegisterFunction(typeof(Sqrt)); // register custom function
con.Open();
SQLiteCommand com = new SQLiteCommand("select sqrt(" + txb_Input.Text.Replace(',', '.') + ")", con); // select result
string res = com.ExecuteScalar().ToString();
txb_Output.Text = res;
}
}
}
Run, try and enjoy.
This is an approximation of sqrt for numbers under 10000. It can be extended for arbitrary numbers, and can be extended to arbitrary precision as needed. This kind of tabular interpolation is what happens in most fast implementations anyway:
case when weight >= 1 and weight<=10 then 1+0.240253073*(weight-1)
when weight>=10 and weight<=100 then 3.16227766+0.075974693*(weight-10)
when weight>=100 and weight<=1000 then 10+0.024025307*(weight-100)
else 31.6227766+0.007597469 *(weight-1000) end
And there's the curious fact that each factor you use in such a power-of-10 square root interpolation table is 0.316227766 times the previous one - so you can make this work for an arbitrarily large number, or even stuff a table full of these values to make it work for any number. (Could that lead to some compression here?)
Or this cute one for log10 of integers, using the length function (an interpolation table might work better here, but I like that log10 and length() are similar, and that this works for any integer - no interpolation needed.
((length(x)+length(x*2)+length(x*3)
+length(x*4)+length(x*5))/5.0)-1.0
A better math head than I can probably come up with better and denser approximations. Considering that most sqrt functions in c use approximations anyway - this is a pretty good solution.
This is the only native way of doing it.
As far as I know - you can't do that using only core functions.
Here is the list of native functions Core functions and a list of aggregate functions Aggregate functions.
To solve your problem, you can write your own UDF (user defined function) as illustrated HERE
Only if math functions are not available... and really only in desperation because this isn't gonna be fast...
-- bisect to find the square root to any tolerance desired
with
input(n) as (select 500), --input
sqrt(lo, hi, guess, n, i) as (
select 1, n, n/2, n, 0 from input
union all
select case when guess*guess < n then guess else lo end,
case when guess*guess < n then hi else guess end,
case when guess*guess < n then (hi+guess)/2.0 else (lo+guess)/2.0 end,
n ,
i +1 from sqrt
where abs(guess*guess - n) > 0.0001), -- tolerance
sqrt_out(x, n) as (select guess, n from sqrt order by sqrt.i desc limit 1)
select * from sqrt_out
2021-03-12 (3.35.0)
Added built-in SQL math functions(). (Requires the -DSQLITE_ENABLE_MATH_FUNCTIONS compile-time option.)
Built-In Mathematical SQL Functions
sqrt(X) Return the square root of X. NULL is returned if X is negative.