2

I'm once again trying to push lots of csv data into a postgres database.

In the past I've created a struct to hold the data and unpacked each column into the struct before bumping the lot into the database table, and that is working fine, however, I've just found pgx.CopyFrom* and it would seem as though I should be able to make it work better.

So far I've got the column headings for the table into a slice of strings and the csv data into another slice of strings but I can't work out the syntax to push this into the database.

I've found this post which sort of does what I want but uses a [][]interface{} rather than []strings.

The code I have so far is

// loop over the lines and find the first one with a timestamp
for {                
        line, err := csvReader.Read()                   
        if err == io.EOF { 
           break
        } else if err != nil {
           log.Error("Error reading csv data", "Loading Loop", err)
        }

       // see if we have a string starting with a timestamp
       _, err := time.Parse(timeFormat, line[0])
       if err == nil {
          // we have a data line
          _, err := db.CopyFrom(context.Background(), pgx.Identifier{"emms.scada_crwf.pwr_active"}, col_headings, pgx.CopyFromRows(line))    
      }
   }

}

But pgx.CopyFromRows expects [][]interface{} not []string.

What should the syntax be? Am I barking up the wrong tree?

2
  • 1) Don't you need to know the types in line before inserting/copying into the DB? 2) CopyFrom is for copying rows (plural): "CopyFrom can be faster than an insert with as few as 5 rows.". It looks like you're basically still doing a single-row insert. I think you should accumulate your rows, figure out their types, then pass those as the [][]interface{} like the method expects. Commented Jun 6, 2022 at 7:18
  • Thanks @ZachYoung. So I guess the question then becomes how do I make a line (which is a []String I think) into []interface{}. The data is timestamp followed by a heap of floats. Commented Jun 6, 2022 at 7:24

1 Answer 1

2

I recommend reading your CSV and creating a []interface{} for each record you read, appending the []interface{} to a collection of rows ([][]interface{}), then passing rows on to pgx.

var rows [][]interface{}

// read header outside of CSV "body" loop
header, _ := reader.Read()

// inside your CSV reader "body" loop...
    row := make([]interface{}, len(record))

    // use your logic/gate-keeping from here

    row[0] = record[0] // timestamp

    // convert the floats
    for i := 1; i < len(record); i++ {
        val, _ := strconv.ParseFloat(record[i], 10)
        row[i] = val
    }

    rows = append(rows, row)

...

copyCount, err := conn.CopyFrom(
    pgx.Identifier{"floaty-things"},
    header,
    pgx.CopyFromRows(rows),
)

I can't mock up the entire program, but here's a full demo of converting the CSV to [][]interface{}, https://go.dev/play/p/efbiFN2FJMi.

And check in with the documentation, https://pkg.go.dev/github.com/jackc/pgx/v4.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you so VERY much for the help. Got me going I think.
@PeterNunn, You are VERY welcome. I don't actually use Postgres (or SQL at all), so I was a little hesitant, but your question and the documentation were clear enough to piece something together.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.