已掉线,重新登录

首页 > 绿虎论坛 > 历史版块 > 编程 > 数据库

如何用 SQL 求出「都关注了某100人」和「都收藏了某100帖子」的所有用户?


『回复列表(165|隐藏机器人聊天)』

120.

@老虎会游泳,我发现sqlite也不比脚本语言慢,生成 2亿行,花了 两分半,是tcc的 7~8 倍耗时

但是没有srand,所以每次结果都有细微差异

$ time sqlite3 -csv ':memory:' <<EOF | wc -lc
WITH
  gen(v) AS (
    SELECT random() % 128 + 128
      FROM generate_series(1, random() % 6 + 20)  -- -5 ~ 5 + 20
  )

SELECT value, v
  FROM generate_series(1, 10000000), gen
EOF

结果:

199993358 2293329326

real    2m30.591s
user    2m32.265s
sys     0m1.437s
(/@Ta/2022-08-11 23:23//)

121.

@无名啊,那么你最后测查询了吗

(/@Ta/2022-08-12 13:36//)

122.

@老虎会游泳,还没。昨晚导入至数据库时,碰到了下列问题:

  1. 如何生成不重复的 (uid, num)(数据库一直报错说碰到很多重复行,违反唯一索引)
  2. 怎么在 shell 中传递两种数据给不同进程?(比如 tcc 第一阶段传递给导入 A 表的 SQLite,第二阶段传递给导入 B 表的
(/@Ta/2022-08-12 13:43//)

123.

@老虎会游泳,关于问题一,我暂时修改成了,有重复的就再生成一次(速度瞬间慢了好多

for (int uid = 1; uid <= 10000000; ++uid) {
    uint64_t flags[4] = {0};
    for (int num, s, y, i = randint(15, 25); i > 0; --i) {
        while (num = randint(0, 255), flags[s = num >> 6] & (y = 1 << (num & 0x3F)));
        printf("%d,%d\n", uid, num);
        flags[s] |= y;
    }
}
(/@Ta/2022-08-12 13:44//)

124.

@老虎会游泳,关于问题二,我尝试了很多写法:

0. 首先,建表(这里没事儿)

sqlite3 test.db \
	'CREATE TABLE a (uid INT, num INT, PRIMARY KEY (uid, num)) WITHOUT ROWID' \
	'CREATE TABLE b (uid INT, num INT, PRIMARY KEY (uid, num)) WITHOUT ROWID'

1. 进程替换,再重定向stdoutstderr

C 源码改造成:先往stdout写,写完就fclose(),然后再往stderr写,写完也fclose()

#define each(p, arr) \
    (typeof(*(arr)) *p = (arr), *_end = (arr) + sizeof(arr) / sizeof(*(arr)); p < _end; ++p)

int main() {
    // 省略...
    
    FILE * outputs[] = {stdout, stderr};
    for each (fp_p, outputs) {
        // 省略...
        fclose(*fp_p);
    }
}

shell执行是这样:

tcc -run main.c \
	 > >(sqlite3 test.db '.import --csv /dev/stdin a') \
	2> >(sqlite3 test.db '.import --csv /dev/stdin b')

结果是,第一个sqlite3 迟迟不结束读取stdin(即使我在c里写了fclose(xxx),导致第二个sqlite3也去写入,最后数据库损坏。

改成这样也没用……

tcc -run main.c \
	 > >(sqlite3 test.db 'begin exclusive' '.import --csv /dev/stdin a' 'commit') \
	2> >(sqlite3 test.db 'begin exclusive' '.import --csv /dev/stdin b' 'commit')

2. 进程替换,直接写入至管道

C 源码改造成:挨个往argv里的所有文件写

int main(int argc, char *argv[]) {
    // 省略...
    
    for (int argi = 1; argi < argc; ++argi) {
        FILE * fp = fopen(argv[argi], "w");
        // 省略...
        fclose(fp);
    }
}

shell执行是这样:

tcc -run main.c \
	>(sqlite3 test.db '.import --csv /dev/stdin a') \
	>(sqlite3 test.db '.import --csv /dev/stdin b')

还是不行,结果类似上面的

3. 每个阶段结束后,写入一行结束标记

C 源码改造成:每次结束后,写入特殊标记

int main() {
    // 省略...
    
    for (int times = 0; times < 2; ++times) {
        // 省略...
        puts("END");
    }
}

shell测试的时候,发现都不行:

tcc -run main.c | {
	sed '/^END$/Q' | wc -l
	sed '/^END$/Q' | wc -l
}

结果:

1199869  # 我只测了 3w 个 uid
0

简化一下问题:

$ seq 10 | { head -n 3; head -n 3; }
1
2
3

???

(/@Ta/2022-08-12 14:13//)

125.

@无名啊,把数字取值范围扩大,重复概率不就变小了

(/@Ta/2022-08-12 14:55//)

126. 赞同老虎的想法,现在我做程序也是,数据库甚至都不连表,能放到程序上做的绝不放到数据库里面去做。
(/@Ta/2022-08-12 15:21//)

127.

@爱特,不连表。。是因为分库了嘛?

否则,你该要连的表,无论如何都要join啊,

为何还要多一个『取出所有「稍后我程序自己去连表的主键ID」列表』呢

(/@Ta/2022-08-12 17:32//)

128.

@老虎会游泳,问题一还不算很要紧。问题二呢?我想尽量通过管道传输,有啥子写法不?

(/@Ta/2022-08-12 17:38//)

129.

@无名啊,我会选择直接用python把生成好的SQL语句写入文件。

(/@Ta/2022-08-12 20:46//)

130.

@老虎会游泳,生成SQL语句?

马也,python 生成 csv 我都嫌慢。。SQL 更不敢想了

(/@Ta/2022-08-12 20:49//)

131.

@无名啊

为何还要多一个『取出所有「稍后我程序自己去连表的主键ID」列表』呢

程序封装的好一点,功能复杂一点,就会发现很多id在你想用它们之前就已经查好放在变量里了,唾手可得,没有必要再专门join。

只有“没有任何封装,每个需求都单独写一个SQL,每个功能都独立出现,不与其他功能进行组合”的编程模式和项目架构,才会出现“多一个查找ID步骤”的烦恼。

比如,要显示这个导航条,我需要帖子所在版块的id。那我会“多一个查找ID的步骤”吗?并不会,因为在显示这个导航条之前,我已经显示了帖子标题,在那时候我就已经用select *同时得到版块的id了。

首页 > 绿虎论坛 > 编程 > 数据库

所以只要程序复杂一点,界面功能全面一点,那些查询要用的id、名称等数据在实现其他功能时肯定早就提前找好了。

(/@Ta/2022-08-12 20:59//)

132.

全靠JOIN解决问题,意味着你需要为每个具体问题写一个单独的SQL。而实际项目是各种需求的组合叠加,“具体问题”的数量以排列组合的数量级增长,写那么多SQL你烦不烦,就看你自己了。

而且,在组合各种功能时,明明在上一个问题里想要的ID都拿到了,在下一个问题里还要JOIN一下重新查一遍,会不会觉得蠢也看你自己了。

(/@Ta/2022-08-12 21:05//)

133.

@无名啊

python 生成 csv 我都嫌慢

随着你的挑三拣四,我对结果已经不感兴趣了。

(/@Ta/2022-08-12 21:08//)

134.

@无名啊,如果你专注于一种解决方案,就算只用最初的PHP,测试也可能早就完成了。

(/@Ta/2022-08-12 21:09//)

135.

@老虎会游泳,会不会他的意思是,连 ORM 里都不用 JOIN,全靠自己手动实现 JOIN,达到所谓的『压力传到应用层服务器上』

(/@Ta/2022-08-12 21:11//)

136.

@无名啊,我就是这个意思啊。既然都ORM了,得到表的一行如此简单,那我为何不用一系列的对象访问来获取我想要的数据,为什么我要手动构造一个JOIN呢?

写一个对象操作链

a->getXxx()->getYyy()->getZzz()

不比手动构造一个JOIN简单多了?

而这个操作链实际执行的时候肯定是转换成多次SELECT查询啊。我觉得应该没有人会编程实现把它转换成单一JOIN语句。而且就算真的转了,不见得比多次SELECT快,处于吃力不讨好。

(/@Ta/2022-08-12 21:26//)

137.

@老虎会游泳,他的意思可能是,ORM 里都不要搞什么链式操作(会产生 JOIN),应该自己取得 IDs,再去写等同于下列SQL的代码?

SELECT *
  FROM 用户信息表
 WHERE 用户ID IN (?, ?, ?, ...)
(/@Ta/2022-08-12 21:25//)

138.

@无名啊,链式操作是多次SELECT查询。我觉得应该没有人会编程实现把它转换成单一JOIN语句。而且就算真的转了,不见得比多次SELECT快,处于吃力不讨好。

(/@Ta/2022-08-12 21:27//)

139.

@无名啊,当你进行以下操作时

a->getXxx()->getYyy()->getZzz()

你进行的实际是

select xxx_id from table_a where id=${a_id};

select yyy_id from table_xxx where id=${xxx_id};

select zzz_id from table_yyy where id=${yyy_id};

select * from table_zzz where id=${zzz_id};

所以你当然可以进行链式操作。怎么会产生JOIN呢?为什么要产生JOIN呢?一层一层根据id查询不简单吗?

(/@Ta/2022-08-12 21:36//)

下一页 上一页 7/9页,共165楼

回复需要登录

7月2日 02:59 星期三

本站由hu60wap6驱动

备案号: 京ICP备18041936号-1