"Автоматична" передача таблиці SQL Server у матрицю F #

У мене є наступний код, який робить імпорт з SQL на F # і переведення інформації в матрицю.

У мене є приклад користувачів з 4 критеріями, але якщо я хочу додати ще два критерії (наприклад, кількість критеріїв може змінитися), мені доведеться це зробити вручну (у цьому коді параметр не виконується автоматично, а ім'я колони повинні бути написані).

Це рішення працює, але мені доведеться вручну перевіряти еволюцію в таблиці SQL і змінити вручну назву стовпців у моєму коді, що незабаром буде дуже болісним. Тому моє запитання:

Ви бачите, як можна автоматично враховувати розміри таблиці SQL і, загалом, у вас є спосіб зробити це імпортування без необхідності вручну вводити назву кожного стовпця ("Criteria0_ID" і т. Д. - це ім'я Колони в SQL ")? Це заздалегідь!

type user = {
    Criteria0_ID :  int;
    Criteria1_ID : int;
    Criteria2_ID : int;
    Criteria3_ID : int}

// Extraction from SQL

module ReadSQl =

    let GetUsers = seq { 
        let connStr = new SqlConnectionStringBuilder(DataSource="localhost\sqlexpress", IntegratedSecurity=true, InitialCatalog="TestExtractionF#")
        use cnn = new SqlConnection(connStr.ConnectionString)
        use cmd = new SqlCommand("SELECT * FROM SyntheseTest", cnn)
        cnn.Open()

        use reader = cmd.ExecuteReader()
        while reader.Read() do
        yield {
               Criteria0_ID = unbox(reader.["Criteria0_ID"])
               Criteria1_ID = unbox(reader.["Criteria1_ID"])
               Criteria2_ID = unbox(reader.["Criteria2_ID"])
               Criteria3_ID = unbox(reader.["Criteria3_ID"])
               }
         }

// Sequence transformation

let UserBase = GetUsers |> Seq.toList

// Creating the matrix

let matrixUA =
    Matrix.ofList
        [//Create list containing rows from the database
        for row in UserBase do
     //For each row, return list of columns (float values)
            yield [ float row.Criteria0_ID; 
                    float row.Criteria1_ID;
                    float row.Criteria2_ID;
                    float row.Criteria3_ID;
                                    ] ]

matrixUA
2

2 Відповіді

Використовуючи FsSql , ви можете написати щось на зразок цього:

let readMatrixUA() =
    let connStr = SqlConnectionStringBuilder(DataSource="localhost\sqlexpress", IntegratedSecurity=true, InitialCatalog="TestExtractionF#")
    use cnn = new SqlConnection(connStr.ConnectionString)
    cnn.Open()
    Sql.execReader (Sql.withConnection cnn) "select * from SyntheseTest" []
    |> Sql.map Sql.asNameValue
    |> Seq.map (Seq.map (snd >> Option.fromDBNull >> Option.getOrDefault >> float))
    |> Matrix.ofSeq
3
додано
Це може бути дуже хороша відповідь, у мене є помилка, яку я не розумію: ExecuteReader потребує відкритого та доступного підключення. З'єднання фактично закрито. Ця помилка, здається, стосується C: \ prg \ FsSql \ FsSql \ FsSql.fs: рядок 193 та C: \ prg \ FsSql \ FsSql \ FsSql.fs: рядок 203.
додано Автор katter75, джерело
Так, дякую вам, що я виправив це, тепер каже нова помилка: вказаний кастинг недійсний (помилка, здається, надходить від execReader)
додано Автор katter75, джерело
Це працює дуже багато чого!
додано Автор katter75, джерело
Ще одне питання: якщо в таблиці SQL у мене є значення "NULL" (тобто користувач не відповідав на питання), чи можу я відступити, щоб у моїй матриці було нулі? (на даний момент у мене є однакова помилка "вказаний кастинг недійсний", як тільки у мене є "NULL" в SQL)
додано Автор katter75, джерело
Велике спасибі, це чудово працює!
додано Автор katter75, джерело
@ katter75: я забув відкрити з'єднання. Відредагував мою відповідь, щоб виправити це.
додано Автор Mauricio Scheffer, джерело
@ katter75: Я не знаю, яким типом база даних намагається читати поля. Цей код намагається прочитати їх безпосередньо як поплавці. Можливо, ви захочете спробувати прочитати їх як int першим, а потім перетворити їх на поплавці.
додано Автор Mauricio Scheffer, джерело
@ katter75: наприклад замість Seq.cast робити Seq.cast >> Seq.map float
додано Автор Mauricio Scheffer, джерело
@ katter75: знову відредаговано для вирішення цієї справи.
додано Автор Mauricio Scheffer, джерело

У поточній версії F # вам завжди потрібно визначити тип user самостійно. Ви можете зробити кілька трюків, щоб зробити копіювання більш приємним. Найпростіше зробити це, використовуючи оператора ? , щоб отримати наступний синтаксис:

let (?) (reader:SqlDataReader) (name:string) : 'T =
  unbox (reader.[name])

 yield { Criteria1_ID = reader?Criteria1_ID
         Criteria2_ID = reader?Criteria2_ID 
         Criteria3_ID = reader?Criteria3_ID }

Ви можете взяти це ще далі - якщо ви відкриєте Microsoft.FSharp.Reflection , ви можете використовувати FSharpType.GetRecordFields , щоб отримати назви полів (використовуючи відображення) і зробити копіювання динамічно - якщо ви використовуєте цей підхід, вам потрібно буде лише змінити декларацію запису. Напевно, наскільки це можливо, ви отримаєте поточну версію F #.

Тим не менш, F # 3.0 (приходить до Visual Studio 11) містить функцію, що називається тип виправлень , яка, по суті, дозволяє імпортувати схему автоматично. Ви можете дізнатися більше про переговори Дона Сима (див. Розділ наприклад, це ) і з документація MSDN (проект) .

1
додано