• Ruby 对 csv 文件进行统计 at 2018年08月04日

    不考虑数据量问题的话,就一个简单的 reduce :

    require 'csv'
    
    CSV.foreach("data.csv", headers: true).reduce({}) do |acc, row|
      priority = row["优先级"]
      acc[priority] = (acc[priority] || 0) + 1
      acc
    end
    

    如果量大并且需要跟其他表结合做统计,导入数据库是更合适的选择。除了用 COPYForeign Data Wrapper 也可以考虑,简单粗暴。

  • 老实说,不会。原因几点:

    1. 越是受众面不广的技术越要跟国外的一手资料。一般来说订阅 Elixir Radar 并定期逛逛 Elixir Forum 足矣。而且像 @Rei 所说 周边的书也已经不少了。

    2. 国内资料太少,考虑到实效性就更少了。因此我一般不会选择看中文的(即使搜索到)。我想近几年愿意吃螃蟹学习 Elixir 的人大多也是这个习惯。所以中文资料就更难有市场。这确实是个恶性循环,但任何圈子的形成都是需要时间的。大部分时候我们是要解决问题,那就得用更有效率的获取知识的方式。

  • @jmmbite 本地试了下你的 SQL ,总时间 3 分半。SSD

    aaa

    我感觉这样已经是最快的方案了,因为执行过程全在 PostgreSQL 内部。其他那种要到数据库外做一圈交互的方式应该性能更低。对比下来你的问题应该更多的在于硬件,所以升级机器吧。

  • 如果要从某个数据源导入,可以生成 csv 文件然后 COPY ... FROMINSERT 能快到哪去……

    如果只是随便生成大量数据,generate_series 函数了解一下。

  • elixir 药丸,赶紧 go 到 go 那边去

  • 所以呢我说的其实是 query builder 的抽象能力。因为大部分 ORM 都有个 query builder 因此它也算是 ORM 的一部分。如果完全没有抽象的话对查询的共用部分就只能拼接 SQL 字符串了。mini_sql 的 builder.where 看起来已经够用了。我再举个 Ecto (Elixir 语言的 data wrapper)的例子,实现 Sam 文中提到的完整 SQL :

    from(
      ftl in TopicLink,
      left_join: ft in assoc(ftl, :link_topic),
      left_join: c in assoc(ft, :category),
      group_by: [ftl.url, ft.title, ftl.title, ftl.link_topic_id, ftl.reflection, ftl.internal, ftl.domain],
      order_by: [desc: sum(ftl.clicks), desc: count(ftl.id)],
      limit: 50,
      select: %{
        url: ftl.url,
        title: coalesce(ft.title, ftl.title),
        link_topic_id: ftl.link_topic_id,
        reflection: ftl.reflection,
        internal: ftl.internal,
        domain: ftl.domain,
        user_id: min(ftl.user_id),
        clicks: sum(clicks)
      }
    )
    |> where([ftl, ...], ftl.topic_id == ^topic_id)
    |> where([_, ft, ...], is_nil(ft.deleted_at))
    |> where([ftl, ...], not (ilike(ftl.url, "%.png") or ilike(ftl.url, "%.jpg") or ilike(ftl.url, "%.gif")))
    |> where([_, ft, ...], coalesce(ft.archetype, "regular") != ^Archetype.private_message)
    |> secure_category(guardian.secure_category_ids)
    |> Repo.all()
    

    拿它举例并不是抬杠或者吹 Ecto 如何,我相信以 Ruby 的灵活性可以做到同样的事情(只是感觉没多少人觉得它很重要)。只是说明语言层面提供的抽象可以用来更好的组织和重用代码。这点我个人还是觉得很重要的。毕竟 SQL 作为声明式的语言不善于表达过程式的代码,而生成 query 的过程恰好是过程式的,应用层的语言擅长此道。

    另外拿我最近做的一个统计做例子:

    query_1 =
      FunnelItem
      |> filter_by_subject(subject_id)
      |> filter_by_type(:fam)
      |> score_btw(3, 4)
    
    query_2 =
      FunnelItem
      |> filter_by_subject(subject_id)
      |> filter_by_type(:ove)
      |> score_btw(6, 7)
    
    query_3 =
      FunnelItem
      |> filter_by_subject(subject_id)
      |> filter_by_type(:puc)
      |> score_eq(4)
    
    [query_1, query_2, query_3]
    |> join_queries()
    |> stat_by_filters(filter_key, filter_value_ids)
    

    上面的例子把部分 where 条件抽成了共用的函数,再自定义了一些函数来组合查询,比如 join_queries

    def join_queries(queries) do
      Enum.reduce(queries, fn query, acc ->
        from(
          [f1, ...] in acc,
          join: ft in ^query,
          on: ft.sample_id == f1.sample_id and ft.subject_id == f1.subject_id
        )
      end)
    end
    
  • @hooopofind_by_sql 可以对付注入,就是不大好看,必须用某个具体的 model 来调用该方法。加上结果是该 model 的实例加上从 SELECT 部分动态生成的属性就非常奇怪了。这个问题可以通过建立一个专门的 stat model 来解决,避免尴尬。另外 sanitize_sql_xxx 的那批方法应该也可以解决注入问题(没试过),不过用起来就非常麻烦了。

    虽然统计不是 ORM 需要解决的问题,一个是 OLAP 一个是 OLTP 。但有时候两者都需要共用一些查询条件和表关联,而且有时候相似的统计 SQL 也会有些代码碎片是重复的,这时候就又回到如何重用的问题上来了。query builder 本身用在构造 SQL 上是合适的,问题是 AR 的 query builder 抽象程度还不够,经常写成了大批的 SQL 片段,也就是你说的 SQL + Rails 的方式。

  • 前后端分离后的前端更应该当作一个独立的应用程序,而不是某个大产品的附属。如果再考虑离线和数据同步的情况,也可以说是一种分布式系统了。因此我觉得考虑是否分离的最大因素,就是前端是否复杂到了需要做成一个独立的应用程序。

    本质上这是衡量软件复杂度和抽象的问题。一方面它取决于实际需求。比如 MVC 分层是 web 框架最佳实践?做个简单的网站也许并不需要,Sinatra 那种在 block 里写实现的方式也许更快; jQuery 难以维护?如果你的需求只是网页上加少数动态组件可能它更简单。另一方面也在于开发者自身的技术背景。同样做个 SPA 在专业前端和顺带写点网页的后端眼里复杂度可能完全不同,因此做出的决策也不同,但结果可能殊途同归。

  • 如果是仅仅是删除旧数据,为什么不用 SQL ?

    1. 如果不是很老的 mac 基本可以放心。我 12 年的 macbook 用到去年,一直都是升级到最新的版本没问题。不过周围也有少数出意外的(基本是比我更老的),备份一次数据再做比较好。
    2. 这是既定事实,无法改变。不如调整自己的心态接受。

    作为软件开发者,我觉得不更新是没法接受的。这意味着没有新特性,bug 不会修复,并且迟早会被抛弃。