选择多对多关系中的所有子集

时间:2012-06-20 10:27:59

标签: sql ruby-on-rails postgresql

在我正在构建的rails 3应用程序中,我有以下三个类:

class Instrument < ActiveRecord::Base
  has_many :parts
  has_many :pieces, through: :parts
end    
class Part < ActiveRecord::Base
  belongs_to :instrument
  belongs_to :piece
end
class Piece < ActiveRecord::Base
  has_many :parts
  has_many :instruments, through: :parts
end

我正在寻找一种方法来选择所有相关乐器是任意一组乐器的子集的乐曲。

为避免混淆,我举一个例子:给出

  • 使用乐器的片段(id:1):[1,2,3],
  • 使用[1,2,3,4]
  • 的片段(id:2)
  • 使用[1,3,4]
  • 的片段(id:3)
  • 片段(id:4),[2],

somequery(1,2,3,4)应该会产生所有四个部分,somequery(1,2,3)只有1个和4个,somequery(1,3,4)只有第3个,而somequery(2)只有4个。

我正在使用rails 3.2并且rails中的解决方案会很棒但是sql也很好,并且最好是postgres,但是如果有一个特定于mysql的解决方案也可以。此外,我没有使用相对较小的数据库(1000多件,50件和15.000件),这些数据库没有经过严格的查询,所以如果查询效率不是最优的那么没问题。

作为最终的免责声明,我知道我在ruby / rails中的方式,但在SQL中相当绿。

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

DROP SCHEMA tmp CASCADE;
CREATE SCHEMA tmp;

SET search_path='tmp';


CREATE TABLE instrument
        ( id INTEGER NOT NULL PRIMARY KEY
        , zname varchar
        );
INSERT INTO instrument(id, zname) VALUES
(1, 'instrument_1'), (2, 'instrument_2')
, (3, 'instrument_3'), (4, 'instrument_4');

CREATE TABLE piece
        ( id INTEGER NOT NULL PRIMARY KEY
        , zname varchar
        );
INSERT INTO piece(id, zname) VALUES
(1, 'piece_1'), (2, 'piece_2'), (3, 'piece_3'), (4, 'piece_4');

CREATE TABLE has_part
        ( piece_id INTEGER NOT NULL
        , instrument_id INTEGER NOT NULL
        , PRIMARY KEY (piece_id,instrument_id)
        );

INSERT INTO has_part(piece_id,instrument_id) VALUES
(1,1), (1,2), (1,3)
, (2,1), (2,2), (2,3), (2,4)
, (3,1), (3,3), (3,4)
, (4,2)
        ;

纯sql(不是双重否定NOT EXISTS , NOT IN()

SELECT zname
FROM piece pp
WHERE NOT EXISTS (
        SELECT * FROM has_part nx
        WHERE nx.piece_id = pp.id
        AND nx.instrument_id NOT IN (1,2,3)
        )
        ;

答案 1 :(得分:0)

尝试这样的事情:

Piece.joins(:instruments).where(:instruments => {:id => [1, 2, 3, 4]})

这会创建一个连接2个表的查询,并选择带有所需ID的乐器,最后将它们包装在ActiveRecord对象中,这样您就可以随时执行任何操作。