Sql 使用Perl实现高效语义三元组,无需外部db服务器

Sql 使用Perl实现高效语义三元组,无需外部db服务器,sql,perl,Sql,Perl,我有几个语义三元组。一些例子: Porky,species,pig // Porky's species is "pig" Bob,sister,May // Bob's sister is May May,brother,Sam // May's borther is Sam Sam,wife,Jane // Sam's wife is Jane ... and so on ... 我将每个三元组存储在6个不同的散列中。例如: $ijk{Porky}{species}{pig} =

我有几个语义三元组。一些例子:

Porky,species,pig // Porky's species is "pig" 
Bob,sister,May // Bob's sister is May 
May,brother,Sam // May's borther is Sam 
Sam,wife,Jane // Sam's wife is Jane 
... and so on ... 
我将每个三元组存储在6个不同的散列中。例如:

$ijk{Porky}{species}{pig} = 1; 
$ikj{Porky}{pig}{species} = 1; 
$jik{species}{Porky}{pig} = 1; 
$jki{species}{pig}{Porky} = 1; 
$kij{pig}{Porky}{species} = 1; 
$kji{pig}{species}{Porky} = 1; 
这让我能够有效地提出以下问题:

  • Porky是什么物种(
    keys%{$ijk{Porky}{species}

  • 列出所有猪(
    keys%{$jki{species}{pig}}

  • 关于波基我有什么信息?(
    keys%{$ijk{Porky}

  • 列出所有物种(
    keys%{$jik{species}}

等等。请注意,上面的示例中没有一个一次遍历一个元素的列表。他们都让我“立刻”找到了答案。换句话说,每个答案都是一个散列值。当然,答案本身可能是一个列表,但我不会遍历任何列表来获得答案

然而,定义6个单独的哈希似乎效率很低。有 无需使用外部数据库引擎即可轻松完成此任务 (对于这个问题,SQLite3算作一个外部数据库引擎)

或者我只是将一小部分SQL复制到Perl中

编辑:我想我想说的是:我喜欢关联数组,但它们似乎是这项工作的错误数据结构。这里正确的数据结构是什么?Perl模块实现了什么?

这里的“高效”一词并不正确,因为您担心提高内存交换的速度,这通常是它的工作方式

唯一的替代方法是将三元组存储为不同的值,然后将三个“索引”放入其中:

$row = [ "Porky", "species", "pig" ];
push @{$subject_index{Porky}}, $row;
push @{$relation_index{species}}, $row;
push @{$target_index{pig}}, $row;
要执行类似“列出所有猪”的操作,您必须找到
$relationship\u index{species}
$target\u index{pig}
的交集。您可以手动执行,也可以使用您喜爱的集合实现执行


然后将其封装在一个漂亮的对象界面中,您基本上实现了
内部连接
:)

散列的单个散列就足够了:

use strict;
use warnings;

use List::MoreUtils qw(uniq);
use Data::Dump qw(dump);

my %data;
while (<DATA>) {
    chomp;
    my ($name, $type, $value) = split ',';
    $data{$name}{$type} = $value;
}

# What species is Porky?
print "Porky's species is: $data{Porky}{species}\n";

# List all pigs
print "All pigs: " . join(',', grep {defined $data{$_}{species} && $data{$_}{species} eq 'pig'} keys %data) . "\n";

# What information do I have on Porky?
print "Info on Porky: " . dump($data{Porky}) . "\n";

# List all species
print "All species: " . join(',', uniq grep defined, map $_->{species}, values %data) . "\n";

__DATA__
Porky,species,pig
Bob,sister,May
May,brother,Sam
Sam,wife,Jane
你考虑过使用吗?它有DBI支持的存储,但也有内存存储,如果需要持久性,可以用RDF/XML、Turtle、N-Triples等进行解析/序列化

例如:

use strict;
use warnings;
use RDF::Trine qw(statement literal);

my $ns   = RDF::Trine::Namespace->new("http://example.com/");
my $data = RDF::Trine::Model->new;

$data->add_statement(statement $ns->Peppa, $ns->species, $ns->Pig);
$data->add_statement(statement $ns->Peppa, $ns->name, literal 'Peppa');
$data->add_statement(statement $ns->George, $ns->species, $ns->Pig);
$data->add_statement(statement $ns->George, $ns->name, literal 'George');
$data->add_statement(statement $ns->Suzy, $ns->species, $ns->Sheep);
$data->add_statement(statement $ns->Suzy, $ns->name, literal 'Suzy');

print "Here are the pigs...\n";
for my $pig ($data->subjects($ns->species, $ns->Pig)) {
  my ($name) = $data->objects($pig, $ns->name);
  print $name->literal_value, "\n";
}

print "Let's dump all the data...\n";
my $ser = RDF::Trine::Serializer::Turtle->new;
print $ser->serialize_model_to_string($data), "\n";
Trine是一个相当大的框架,因此有一点编译时代价。但在运行时,速度相对较快

如果希望使用SPARQL查询数据,可以将RDF::Trine与RDF::Query结合使用

use RDF::Query;

my $q = RDF::Query->new('
  PREFIX : <http://example.com/>
  SELECT ?name
  WHERE {
    ?thing :species :Pig ;
           :name ?name .
  }
');

my $r = $q->execute($data);

print "Here are the pigs...\n";
while (my $row = $r->next) {
  print $row->{name}->literal_value, "\n";
}
使用RDF::Query;
我的$q=RDF::查询->新建
前缀:
选择?名称
在哪里{
?事物:种类:猪;
:name?name。
}
');
我的$r=$q->execute($data);
打印“这是猪…\n”;
while(my$row=$r->next){
打印$row->{name}->literal_值“\n”;
}
查询同时支持SPARQL 1.0和SPARQL 1.1。Trine和RDF::Query都是由SPARQL 1.1工作组成员Gregory Williams编写的。查询是在SPARQL 1.1查询测试套件上实现100%的首批实现之一。(这甚至可能是第一次?)

或者我只是将一小部分SQL复制到Perl中

开始使用实际的SQL非常容易,使用SQLite内存数据库

#!/usr/bin/perl                                                                                                                
use warnings; use strict;

use DBI;

my $dbh = DBI->connect("dbi:SQLite::memory:", "", "", {
    sqlite_use_immediate_transaction => 0,
    RaiseError => 1,
});

$dbh->do("CREATE TABLE triple(subject,predicate,object)");
$dbh->do("CREATE INDEX 'triple(subject)' ON triple(subject)");
$dbh->do("CREATE INDEX 'triple(predicate)' ON triple(predicate)");
$dbh->do("CREATE INDEX 'triple(object)' ON triple(object)");

for ([qw<Porky species pig>],
     [qw<Porky color pink>],
     [qw<Sylvester species cat>]) {
    $dbh->do("INSERT INTO triple(subject,predicate,object) VALUES (?, ?, ?)", {}, @$_);
}

use JSON;
print to_json( $dbh->selectall_arrayref('SELECT * from triple WHERE predicate="species"', {Slice => {}}) );

然后,您可以以熟悉的方式查询和索引数据。可扩展性也很好。

我认为您正在混合类别和值,例如name=Porky和species=pig

举个例子,我会这样说:

    my %hash;

    $hash{name}{Porky}{species}{pig} = 1;
    $hash{species}{pig}{name}{Porky} = 1;
    $hash{name}{Bob}{sister}{May} = 1;
    $hash{sister}{May}{name}{Bob} = 1;
    $hash{name}{May}{brother}{Sam} = 1;
    $hash{brother}{Sam}{name}{May} = 1;
    $hash{name}{Sam}{wife}{Jane} = 1;
    $hash{wife}{Jane}{name}{Sam} = 1;

是的,这有一些明显的冗余,因为我们可以很容易地将大多数名称与其他值区分开来。但是第三级散列键也是一个顶级散列键,可以用来获取有关某个元素的更多信息。

这或多或少就是列上的索引的工作原理,是的。那么,纯Perl中有没有更好的方法来实现这一点呢?使用对象而不是散列。(说到“高效”,我想你指的是作为程序员的高效,而不是CPU时钟周期。)@ThisSuitesBlack你能说得更多吗?物体不会让事情变得更糟吗?我的意思是两方面都有效率。显然,我可以(并且已经)创建一个循环来完成这六个作业。然而,我感觉到我在强迫关联数组成为它们从来就不应该是的东西。使用对象的非常简单的例子(你必须自己实现这个类):
my$porky=Pig->new(name=>porky');说$porky->name,$porky->species我仍然认为“应该”有一些Perl实现的数据结构,我只需要输入“Porky,species,pig”一次,其他一切都会“神奇地”发生?就像某种数据库,关系?好的,但是Perl grep不是一次只遍历一个元素吗?我试图用散列来避免类似的事情。好吧,但你为什么要避免类似的事情呢?除非您有一个特定的需要,您试图解决,保持代码尽可能简单。这提高了可读性和可维护性。OP在问题中明确排除了SQLite:“有没有更简单的方法不用使用外部数据库引擎(对于这个问题,SQLite3算作外部数据库引擎)?”是的。我是说SQL可能已经被过早地排除在外了。此解决方案使用内存中的数据库,并展示了此类解决方案的轻量级。
    my %hash;

    $hash{name}{Porky}{species}{pig} = 1;
    $hash{species}{pig}{name}{Porky} = 1;
    $hash{name}{Bob}{sister}{May} = 1;
    $hash{sister}{May}{name}{Bob} = 1;
    $hash{name}{May}{brother}{Sam} = 1;
    $hash{brother}{Sam}{name}{May} = 1;
    $hash{name}{Sam}{wife}{Jane} = 1;
    $hash{wife}{Jane}{name}{Sam} = 1;