«Автоматический» перенос таблицы SQL Server в матрицу F #

#sql-server #f# #import #matrix

#sql-сервер #f# #импорт #матрица

Вопрос:

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

Здесь у меня есть пример пользователей, имеющих 4 критерия, но если я хочу добавить, например, еще 2 критерия (и количество критериев может измениться) Мне придется сделать это вручную (в этом коде измерение не выполняется автоматически, и должны быть записаны имена столбцов).

Это решение работает, но мне приходится вручную проверять изменения в таблице 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="localhostsqlexpress", 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
  

Ответ №1:

Используя FsSql, вы можете написать что-то вроде этого:

 let readMatrixUA() =
    let connStr = SqlConnectionStringBuilder(DataSource="localhostsqlexpress", 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
  

Комментарии:

1. Это может быть очень хорошим ответом, у меня просто ошибка, которую я не понимаю: ExecuteReader требуется открытое и доступное соединение. Соединение фактически закрыто. Эта ошибка, похоже, относится к C:prgFsSqlFsSqlFsSql.fs:line 193 и C:prgFsSqlFsSqlFsSql.fs:line 203.

2. @katter75 : я забыл открыть соединение. Отредактировал мой ответ, чтобы исправить это.

3. Да, спасибо, я исправил это, теперь появляется новая ошибка: указанное приведение недопустимо (ошибка, похоже, исходит от execReader)

4. @katter75: я не знаю, с каким типом база данных пытается считывать поля. Этот код пытается прочитать их непосредственно как значения с плавающей запятой. Возможно, вы захотите сначала прочитать их как int, а затем преобразовать их в значения с плавающей запятой.

5. @katter75: например, вместо Seq.cast do Seq.cast<int> >> Seq.map float

Ответ №2:

В текущей версии 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 (черновик).