4

I have a sqlite table with a mix of integer and float columns. I'm trying to get the max and min values of each column. For integer columns the following code works but I get a cast error when using the same code on float columns:

let numCats = query{for row in db do minBy row.NumCats}

For float columns I'm using the following code but it's slow.

let CatHight = query{for row in db do select row.CatHeight} |> Seq.toArray |> Array.max

I have 8 integer columns and 9 float columns and the behavior has been consistent across all columns so that's why I think it's an issue with the column type. But I'm new to F# and don't know anything so I'm hoping you can help me.

Thank you for taking the time to help, it's much appreciated.

SQLProvider version: 1.0.41

System.Data.SQLite.Core version: 1.0.104

The error is: System.InvalidCastException occurred in FSharp.Core.dll

Added Information

I created a new table with one column of type float. I inserted the values 2.2 and 4.2. Using SQLProvider and System.Data.SQLite.Core I connected queried the database using minBy or maxBy and I get the cast exception. If the column type is integer it works correctly.

More Added Information

Exception detail:

System.Exception was unhandled Message: An unhandled exception of type 'System.Exception' occurred in >FSharp.Core.dll Additional information: Unsupported execution expression value(FSharp.Data.Sql.Runtime.QueryImplementation+SqlQueryable1[FSharp.>Data.Sql.Common.SqlEntity]).Min(row => >Convert(Convert(row.GetColumn("X"))))`

Code that fails:

open FSharp.Data.Sql

[<Literal>]
let ConnectionString =
"Data Source=c:\MyDB.db;" +
"Version=3;foreign keys=true"

type Sql = SqlDataProvider<Common.DatabaseProviderTypes.SQLITE,
ConnectionString,
//ResolutionPath = resolutionPath,
CaseSensitivityChange = Common.CaseSensitivityChange.ORIGINAL>

let ctx = Sql.GetDataContext()
let Db = ctx.Main.Test

let x = query{for row in Db do minBy row.X}
printfn "x: %A" x

Update 2/1/17

Another user was able to reproduce the issue so I filed an Issue with SQLProvider. I'm now looking at workarounds and while the following code works and is fast, I know there's a better way to do it - I just can't find the correct way. If somebody answers with better code I'll accept that answer. Thanks again for all the help.

let x = query {for row in db do
                sortBy row.Column
                take 1
                select row.Column } |> Seq.toArray |> Array.min
  • have you tried wrapping the output in `float` (to coerce). E.g. `float row.NumCats` and `float row.CatHeight`? – s952163 Jan 30 '17 at 23:32
  • @s952163 - I tried 'let aAFlow:float = query{for row in Db do minBy row.AvgMlAf}:float' with the same result but I don't think my implementation is correct either. It does correctly type the statement in code editor. – speedycorndog Jan 30 '17 at 23:43
  • 1
    Can you give a little more information? – FoggyFinder Jan 31 '17 at 17:16
  • @ Foggy Finder - I added more information, hopefully it helps, if there's something specific you need I'll try to provide it. Thank you. – speedycorndog Jan 31 '17 at 18:15
  • 1
    great, but I mean message of exception and some code around it – FoggyFinder Jan 31 '17 at 18:17
  • 1
    I added more exception info and the entire code I used to test it – speedycorndog Jan 31 '17 at 18:56
  • can you put the db on github for example. Although you might have Fsharp.Core mismatch or some other issue. – s952163 Jan 31 '17 at 23:22
  • 1
    I can reproduce your issue with Sqlite and latest versions of sqlprovider and sqlite.core. Maybe you should file an Issue. Right now what you can do is pipe the query into a `|> Seq.minBy`, but as you say performance-wise it might be difficult. – s952163 Feb 01 '17 at 07:02
  • btw, I recommend joining to the [F# Slack](http://fsharp.org/guides/slack/) and you can also join to the [SO chat](http://chat.stackoverflow.com/rooms/51909/f) – FoggyFinder Feb 01 '17 at 15:16

2 Answers2

2

This is my workaround that @s952163 and good people in the SO f# chat room helped me with. Thanks again to everyone who helped.

let x = query {for row in db do
                sortBy row.Column
                take 1
                select row.Column } |> Seq.head
  • Can you check 1.0.42, seems like the [fix is in](https://github.com/fsprojects/SQLProvider/commit/4b176dd487f09c239fd21451940bc71c9402ffd1) – s952163 Feb 04 '17 at 02:02
0

You need to coerce the output column to int or float (whichever you need or is giving trouble to you). You also need to take care in case any of your columns are nullable. The example below will coerce the column to float first (to take care of being nullable), then convert it to int, and finally get the minimum:

let x = query { for row in MYTABLE do
                minBy (int (float row.MYCOLUMN))}

You might want to change the order of course, or just say float Mycolumn.

Update: With Sqlite it indeed causes an error. You might want to do query { ... } |> Seq.minBy to extract the smallest number.

s952163
  • 6,276
  • 4
  • 23
  • 47
  • thank you for the correct syntax but unfortunately that fails also. I've also tried with just one row in my db to make sure values were all decimal if the float columns. If there's another way to go about this I'm open to anything. – speedycorndog Jan 31 '17 at 14:48