SQL-多对多替代方案?

时间:2019-01-22 11:13:38

标签: mysql sql many-to-many

假设我有记录:

======= =========
Element id
======= =========
        "H"
        "O"

另一个类似:

======== ==
Compound id
======== ==
         "Water"

使用:

======== == =========== ========== ==========
Relation id compound_id element_id bond
======== == =========== ========== ==========
         1  "Water"     "H"        "Covalent"
         2  "Water"     "H"        "Covalent"
         3  "Water"     "O"        "Covalent"

现在,我的查询中大多数都不是完全匹配的,但是假设有时我想找到具有确切元素= ["H", "H", "O"]的化合物(即,而不是 Hydrooxide ["H", "O"])或 Peroxide ["H", "H", "O", "O"])。

我该怎么办?

2 个答案:

答案 0 :(得分:3)

为什么不只使用array_agg()

select compound_id
from t3
group by compound_id
having array_agg(element_id order by element_id) = array['H', 'H', 'O']

答案 1 :(得分:1)

始终最好使数据库规范化。在您的特定情况下,我将存储每个化合物的元素数量,而不是为每个元素添加新行。

#include <map>
#include <functional>
#include <atomic>

template <typename State, typename Transition>
class fsm
{
    using handler_t = std::function<void()>;

    class from_t
    {
        Transition transition_;
        State state_;

    public:
        from_t(Transition transition, State state) :
            transition_{ transition },
            state_{ state }
        {
        }

        bool operator<(const from_t& other) const
        {
            if (transition_ < other.transition_)
            {
                return true;
            }
            else if (transition_ > other.transition_)
            {
                return false;
            }
            else
            {
                return state_ < other.state_;
            }
        }
    };

    class to_t
    {
        State state_;
        handler_t handler_;

    public:
        to_t(State state, handler_t handler) :
            state_{ state },
            handler_{ handler }
        {
        }

        State state() const { return state_; }

        void operator()() const { handler_(); }
    };

    std::map<from_t, to_t> transitions_;
    std::atomic<State> state_;

public:
    fsm(State initial_state) :
        state_{ initial_state }
    {
    }

    void add(State from, Transition transition, State to, handler_t handler)
    {
        transitions_.insert({ { transition, from }, { to, handler } });
    }

    void add(State from, Transition transition, State to)
    {
        add(transition, to, from, [] {});
    }

    bool to(Transition transition)
    {
        auto found = transitions_.find({ transition, state_ });

        if (found != transitions_.end())
        {
            auto& to = found->second;

            state_ = to.state();

            to();

            return true;
        }
        else
        {
            return false;
        }
    }
};

确切匹配的查询将是

 compound_id element_id      bond         count
 -------------------------------------------------
   "Water"     "H"        "Covalent"        2
   "Water"     "O"        "Covalent"        1

但是,由于将使用顺序扫描,因此该方法不是最佳的。如果非正规化不是问题,则可以为每个化合物存储许多不同的元素。

 select compound_id
 from elements
 group by compound_id
 having count(
              case when 
                (element_id = 'H' and count = 2) or
                (element_id = 'O' and count = 1) then 1 
              end
        ) = count(*)

然后查询可能是

 compound_id   element_count
 ------------------------------
   "Water"          2

并且如果您在 select e.compound_id from elements e join compounds c on e.compound_id = c.compound_id where c.element_count = 2 and ((e.element_id = 'H' and e.count = 2) or (e.element_id = 'O' and e.count = 1)) group by e.compound_id having count(*) = 2 compounds(element_count)上都有索引,那么即使数据库很大,查询也将使用它快速检索结果。