【问题标题】:Insert an array of tables into one table SQLite C/C++将一组表插入到一个表中 SQLite C/C++
【发布时间】:2012-12-20 19:54:56
【问题描述】:

我制作了自己的数据库格式,遗憾的是它需要太多的内存,而且它的大小变得非常可怕,而且维护起来也很糟糕。

所以我正在寻找一种将对象中的结构数组存储到表中的方法。

我猜我需要使用 blob,但欢迎使用所有其他选项。实现 blob 的简单方法也会有所帮助。

我已经附上了我的保存代码和相关结构(从我之前可怕的帖子更新)

#include "stdafx.h"
#include <string>
#include <stdio.h>
#include <vector>
#include "sqlite3.h"
using namespace std;
struct PriceEntry{

    float cardPrice;
    string PriceDate;
    int Edition;
    int Rarity;
};
struct cardEntry{
    string cardName;
    long pesize;
    long gsize;
    vector<PriceEntry> cardPrices;
    float vThreshold;
    int fav;
};


vector<cardEntry> Cards;

void FillCards(){
  int i=0;
  int j=0;
  char z[32]={0};
  for(j=0;j<3;j++){
     cardEntry tmpStruct;
     sprintf(z, "Card Name: %d" , i);
     tmpStruct.cardName=z;
     tmpStruct.vThreshold=1.00;
     tmpStruct.gsize=0;
     tmpStruct.fav=1;
            for(i=0;i<3;i++){
                PriceEntry ss;
                ss.cardPrice=i+1;
                ss.Edition=i;
                ss.Rarity=i-1;

                sprintf(z,"This is struct %d", i);

                ss.PriceDate=z;
                tmpStruct.cardPrices.push_back(ss);
            }

            tmpStruct.pesize=tmpStruct.cardPrices.size();
            Cards.push_back(tmpStruct);
  }
}
int SaveCards(){
      // Create an int variable for storing the return code for each call
    int retval;
    int CardCounter=0;
    int PriceEntries=0;
    char tmpQuery[256]={0};

    int q_cnt = 5,q_size = 256;


    sqlite3_stmt *stmt;


    sqlite3 *handle;


    retval = sqlite3_open("sampledb.sqlite3",&handle);

    if(retval)
    {
        printf("Database connection failed\n");
        return -1;
    }
    printf("Connection successful\n");

    //char create_table[100] = "CREATE TABLE IF NOT EXISTS users (uname TEXT PRIMARY KEY,pass TEXT NOT NULL,activated INTEGER)";
    char create_table[] = "CREATE TABLE IF NOT EXISTS Cards (CardName TEXT, PriceNum NUMERIC, Threshold NUMERIC, Fav NUMERIC);";


    retval = sqlite3_exec(handle,create_table,0,0,0);
    printf( "could not prepare statemnt: %s\n", sqlite3_errmsg(handle) );
    for(CardCounter=0;CardCounter<Cards.size();CardCounter++){
        char Query[512]={0};

        for(PriceEntries=0;PriceEntries<Cards[CardCounter].cardPrices.size();PriceEntries++){

              //Here is where I need to find out the process of storing the vector of PriceEntry for Cards then I can modify this loop to process the data

       }

            sprintf(Query,"INSERT INTO Cards VALUES('%s',  %d, %f, %d)",            
            Cards[CardCounter].cardName.c_str(),
            Cards[CardCounter].pesize,
            Cards[CardCounter].vThreshold,
            Cards[CardCounter].fav); //My insert command  

        retval = sqlite3_exec(handle,Query,0,0,0);
        if(retval){

            printf( "Could not prepare statement: %s\n", sqlite3_errmsg(handle) );

        }
    }
    // Insert first row and second row

    sqlite3_close(handle);
    return 0;
}

我尝试使用谷歌搜索,但结果还不够。

【问题讨论】:

  • 如果您至少提供一小部分“您目前拥有的”,那就太好了。我可以建议您开始使用具有某种含义的变量名称吗?我确定您知道 E、R 和 cP 代表什么,但没有其他人知道。它们不必是很长的名称,只需 3-5 个字符。
  • (只是猜测)那么你是否将'cEy'元素存储在'cEy'表的不同sqlite列中,你的问题是如何在sqlite列中实际存储'PE'值数组?如果是这样,那么您需要一种方法来“序列化”一个 PE(以及它们的向量),然后将序列化数据存储为 BLOB 列。
  • @MatsPetersson 代码已更新 :) @Gigi 如果我必须走 blob 路线,我会将所有信息放入带有标题的字符缓冲区中,以便区分条目。从来不需要序列化,所以不确定它在代码方面意味着什么。我希望 SQL 有某种更简单的方法。
  • “序列化”只是将某些内容转换为可以存储在某处的“表示”。对于像您这样的简单情况,并且忽略字节顺序,您只需将 pod PE 成员的二进制表示附加到 char 向量,加上字符串“PriceDate”大小,然后是该字符串中的每个字符。然后将其写入blob列。同样对于反序列化,只需执行与此相反的操作。

标签: c++ sql c arrays sqlite


【解决方案1】:

这里有两种类型:卡片和价格条目。每张卡片可以有多个 PriceEntries。

您可以将卡片存储在一张表中,每行一张卡片。但是您对如何存储 PriceEntries 感到困惑,对吧?

您通常在此处为 PriceEntries 设置第二个表,将 Cards 表的唯一一列(或多列)作为键。我猜 CardName 对于每张卡都是唯一的?让我们一起去吧。因此,您的 PriceEntry 表将有一个 CardName 列,然后是 PriceEntry 信息列。每个 PriceEntry 都有一行,即使 CardName 列中有重复项。

PriceEntry 表可能如下所示:

CardName  | Some PE value  | Some other PE value
Ace       | 1 | 1
Ace       | 1 | 5
2         | 2 | 3

等等。所以当你想找到一张卡片的 PriceEntries 数组时,你会这样做

select * from PriceEntry where CardName = 'Ace'

从上面的示例数据中,您将返回 2 行,您可以将其放入一个数组中(如果您愿意的话)。

不需要 BLOB!

【讨论】:

  • 哇!太棒了!谢谢
【解决方案2】:

这是一个简单的序列化和反序列化系统。 PriceEntry 类已通过序列化支持进行了扩展(非常简单)。现在您所要做的就是将 PriceEntry(或其中的一组)序列化为二进制数据并将其存储在 blob 列中。稍后,您将获得 blob 数据并从中反序列化具有相同值的新 PriceEntry。底部给出了如何使用它的示例。享受吧。

#include <iostream>
#include <vector>
#include <string>
#include <cstring> // for memcpy

using std::vector;
using std::string;

// deserialization archive
struct iarchive
{
    explicit iarchive(vector<unsigned char> data)
    : _data(data)
    , _cursor(0)
    {}

    void read(float& v)          { read_var(v); }
    void read(int& v)            { read_var(v); }
    void read(size_t& v)         { read_var(v); }
    void read(string& v)         { read_string(v); }

    vector<unsigned char> data() { return _data; }

private:

    template <typename T>
    void read_var(T& v)
    {
      // todo: check that the cursor will not be past-the-end after the operation

      // read the binary data
      std::memcpy(reinterpret_cast<void*>(&v), reinterpret_cast<const void*>(&_data[_cursor]), sizeof(T));

      // advance the cursor
      _cursor += sizeof(T);
    }

    inline

    void
    read_string(string& v)
    {  
      // get the array size
      size_t sz;
      read_var(sz);

      // get alignment padding
      size_t padding = sz % 4;
      if (padding == 1) padding = 3;
      else if (padding == 3) padding = 1;

      // todo: check that the cursor will not be past-the-end after the operation

      // resize the string
      v.resize(sz);

      // read the binary data
      std::memcpy(reinterpret_cast<void*>(&v[0]), reinterpret_cast<const void*>(&_data[_cursor]), sz);

      // advance the cursor
      _cursor += sz + padding;
    }

    vector<unsigned char> _data;    // archive data
    size_t _cursor;                 // current position in the data
};


// serialization archive
struct oarchive
{
    void write(float v)          { write_var(v); }
    void write(int v)            { write_var(v); }
    void write(size_t v)         { write_var(v); }
    void write(const string& v)  { write_string(v); }

    vector<unsigned char> data() { return _data; }

private:

    template <typename T>
    void write_var(const T& v)
    {
      // record the current data size
      size_t s(_data.size());

      // enlarge the data
      _data.resize(s + sizeof(T));

      // store the binary data
      std::memcpy(reinterpret_cast<void*>(&_data[s]), reinterpret_cast<const void*>(&v), sizeof(T));
    }

    void write_string(const string& v)
    {
      // write the string size
      write(v.size());

      // get alignment padding
      size_t padding = v.size() % 4;
      if (padding == 1) padding = 3;
      else if (padding == 3) padding = 1;

      // record the data size
      size_t s(_data.size());

      // enlarge the data
      _data.resize(s + v.size() + padding);

      // store the binary data
      std::memcpy(reinterpret_cast<void*>(&_data[s]), reinterpret_cast<const void*>(&v[0]), v.size());
    }

    vector<unsigned char> _data;     /// archive data
};



struct PriceEntry
{
    PriceEntry()
    {}

    PriceEntry(iarchive& in) // <<< deserialization support
    {
        in.read(cardPrice);
        in.read(PriceDate);
        in.read(Edition);
        in.read(Rarity);
    }

    void save(oarchive& out) const // <<< serialization support
    {
        out.write(cardPrice);
        out.write(PriceDate);
        out.write(Edition);
        out.write(Rarity);
    }

    float cardPrice;
    string PriceDate;
    int Edition;
    int Rarity;
};



int main()
{    
    // create a PriceEntry
    PriceEntry x;
    x.cardPrice = 1;
    x.PriceDate = "hi";
    x.Edition = 3;
    x.Rarity = 0;

    // serialize it
    oarchive out;   
    x.save(out);

    // create a deserializer archive, from serialized data
    iarchive in(out.data());

    // deserialize a PriceEntry
    PriceEntry y(in);

    std::cout << y.cardPrice << std::endl;
    std::cout << y.PriceDate << std::endl;
    std::cout << y.Edition << std::endl;
    std::cout << y.Rarity << std::endl;
}

【讨论】:

  • 如果真的有人这样做,至少添加一个版本号作为保存的第一个字段,以便在更改或添加字段时可以这样做并保持向后兼容性。 (表类数据的 BLOB?OP 知道这是错误的方法。您有一个带有查询语言的数据库,不妨按预期使用它!)
猜你喜欢
  • 1970-01-01
  • 2018-10-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-05
  • 1970-01-01
  • 2021-01-20
相关资源
最近更新 更多