F# Query Expression / select operator / changing column headings in result - f#

In the following code:
#r "System.Data.dll"
#r "FSharp.Data.TypeProviders.dll"
#r "System.Data.Linq.dll"
open System
open System.Data
open System.Data.Linq
open Microsoft.FSharp.Data.TypeProviders
open Microsoft.FSharp.Linq
open System.Windows.Forms
type dbSchema = SqlDataConnection<"...">
let grid<'T> (x:seq<'T>) =...
let query1 =
query {
for row in db.Status do
//select row
select (row.StatusID, row.Name)
}
query1 |> Seq.toArray |> grid
What are the better ways to change columns to meaningful headings (e.g. actual Column from data source) instead of of just (Item1 Item2...).
Note: For grid function, please see Tomas Petricek response.
Regards,
IP

You can select the data as a record to let DataGridView infer the names of the columns
type Status =
{
StatusID: int
Name: string
}
let query1 =
query {
for row in db.Status do
select { StatusID = row.StatusID; Name = row.Name }
}

This should a bit nicer now in F# 4.6 using the new Anonymous Record feature.
let query1 =
query {
for row in db.Status do
//select row
select {| StatusID = row.StatusID; Name = row.Name; |}
}
Notice the use of the | character on the insides of the curly braces. This is what now distinguishes regular records from anonymous records in F#. Also notice that the property names StatusID and Name must be redundantly specified in the current form. Although this is similar to how constructing objects works in JavaScript, .NET Developers have been enjoying a more abbreviated syntax through C# anonymous types. The feature to allow for similar implicit property names is suggested as a followup change in future language specifications.

Related

Operator/operand type mismatch

I'm using the Visual FoxPro OLE DB provider to query a VFP DB and I'm getting:
System.Data.OleDb.OleDbException
'Operator/operand type mismatch`
What could I be doing wrong?
In my where clause I had an int on one side and a char(15) on the other side
Table Schema
id int
Query
SELECT *
FROM [some-table]
WHERE id = 'some string'
In my case to avoid such kind of inconveniences I do the following I hope it works for you:
var_name = iif(vartype(var_name)=='N',var_name,Val(var_name))
so you avoid two possible errors, if it comes in character with value I convert it into number and if it comes in character without any value it becomes 0.
SELECT *
FROM [some-table]
WHERE id = ?Var_name

Sqlite arithmetic column of type real is restricted to 7 digits

I have constructed a simple Xamarin IOS Application to test the problem.
Lets say I create an sqlite table test with two “numeric columns”
I declare the first one named as Value1 as real(25,10) and the second one named as Value2 as NUMERIC(25,10) by running the following code
public void CreateTable()
{
SqliteCommand SQLcommand = new SqliteCommand();
SQLcommand = this.Connection.CreateCommand();
SQLcommand.CommandText = "CREATE TABLE IF NOT EXISTS test ( Val1 real(25, 10) NOT NULL COLLATE BINARY DEFAULT (0.0), Val2 Numeric(25, 10) NOT NULL COLLATE BINARY DEFAULT (0.0));";
SQLcommand.ExecuteNonQuery();
SQLcommand.Dispose();
}
I insert the number 2300450,25 into both of them
When I try to read it from the database then I get
Value1 -> 2300450
Value2 -> 2300450,25
If I insert the number 23004,25 all is good!!!
Value1 -> 23004,25
Value2 -> 23004,25
the values are inserted using the following code
public void IntertData(object value)
{
SqliteCommand cmd = new SqliteCommand();
cmd = this.Connection.CreateCommand();
cmd.CommandText = "insert into test (Val1,Val2) values (#val1,#val2)";
cmd.Parameters.AddWithValue("#val1", value);
cmd.Parameters.AddWithValue("#val2", value);
cmd.ExecuteNonQuery();
cmd.Dispose();
}
and the data are loaded into a DataTable using the following code
public DataTable LoadQueryDataTable(string a_Command)
{
SqliteCommand cmd = this.Connection.CreateCommand();
cmd.CommandText = a_Command;
cmd.CommandType = CommandType.Text;
SqliteDataAdapter dat = new SqliteDataAdapter();
dat.SelectCommand = cmd;
DataTable dt = new DataTable();
dat.Fill(dt);
return dt;
}
I have run similar tests in a standard windows forms project and I see no problem there. Therefore I think there is a problem in the mono.data.sqlite implementation.
I would use the numeric declaration, however, it has another problem. According to sqlite documentation numeric is a type affinity
In order to maximize compatibility between SQLite and other database
engines, SQLite supports the concept of "type affinity" on columns.
The type affinity of a column is the recommended type for data stored
in that column. The important idea here is that the type is
recommended, not required. Any column can still store any type of
data. It is just that some columns, given the choice, will prefer to
use one storage class over another. The preferred storage class for a
column is called its "affinity".
Therefore, Numeric cannot be used because in some cases it converts all datta of the column into integers
A column with NUMERIC affinity may contain values using all five
storage classes. When text data is inserted into a NUMERIC column, the
storage class of the text is converted to INTEGER or REAL (in order of
preference)

How to use SQL IN statement in fsharp.data.sqlclient?

I have the following sample code. The objective is to run SQL statement with multiple input parameters.
[<Literal>]
let connectionString = #"Data Source=Localhost;Initial Catalog=Instrument;Integrated Security=True"
[<Literal>]
let query = "SELECT MacroName, MacroCode FROM Instrument WHERE MacroCode IN (#codeName)"
type MacroQuery = SqlCommandProvider<query, connectionString>
let cmd = new MacroQuery()
let res = cmd.AsyncExecute(codeName= [|"CPI";"GDP"|]) |> Async.RunSynchronously
However, codeName is inferred to be string type instead of an array or list and give me an error.
Alternatively, I could run the query without where statement and filter based on the result. However, in lots of other cases that returns millions of rows, I would prefer filter data at the SQL server level to be more efficient.
I didn't find any relevant samples on the documentation of fsharp.data.sqlclient. Please help!
"See Table-valued parameters (TVPs)" section in the documentation:
http://fsprojects.github.io/FSharp.Data.SqlClient/configuration%20and%20input.html
If you have an upper bound n on the values in the IN list, you could just make n parameters. If that's unmanageable, I'm afraid the TVP suggestion is your best option. The reason the FSharp.Data.SqlClient library is unlikely to ever support this directly is because the types are generated based on results from sp_describe_undeclared_parameters; there's no T-SQL parser. We had a single digit upper bound in this scenario and were loathe to change the database, so this option worked for us.
You can use STRING_SPLIT to abstract away the use of table-valued parameters. It seems you have to also declare the param first.
DECLARE #param varchar(1000) = #commaSeparatedList
SELECT Col1 FROM MyTable
WHERE Col2 IN (
SELECT value FROM STRING_SPLIT(#param, ',')
)

GroupBy in F# and Average to values for each group

I am a brand new to F#, and I am having trouble with a simple first query. I have a data set, and I want to group the dollar amount based on the codes (which repeat in the data). Then, for each group I want the average (and eventually standard deviation) of the dollar amounts for each group. Also, I only want to look at ONE providerID, hence the 'where' clause. From my research, I have gotten this far:
let dc = new TypedDataContext()
let query2 = query { for x in dc.MyData do
groupBy x.Code into g
where (x.ProviderId = "some number of type string")
let average = query { for n in g do
averageBy n.DollarAmt }
select (g.Key, average) }
System.Console.WriteLine(query2)
With this I get a compiling error that says, "The namespace or module 'x' is not defined."
I do not understand this because when I ran the query that only collected the data with the specified providerID, it did not complain about this 'x', and I followed the same format with this 'x' for this larger query.
Any ideas? Thank you in advance.
From #kvb's comment: After the groupBy you can only access the group g, not the individual items x. Try putting the where before the groupBy.

How to write join with composite key clause in FSharp query expressions?

How to write this C# join with composite key clause in F#? :
join k in DataContext.Catalogs on
new { id = o.IDENT, v = o.VZ } equals
new { id = k.IDENT, v = k.VZ }
This is similiar question to this:
groupby multiple columns in a F# 3.0 query
which is still not answered. But I can't believe it is not easy possible to write it in FSharp.
Thanks
Use tuples containing the key fields you want:
query {
for o in DataContext.OTable do
join k in DataContext.Catalogs on
((o.IDENT, o.VZ) = (k.IDENT, k.VZ))
select (o.IDENT, k.VZ)
}
Note that you can't create an anonymous type with named fields in F#, like you can in C#. Tuples are probably the closest and most idiomatic translation. See Tomas's answer here.

Resources