List Haskell,如何实现类似SQL的操作?

List Haskell,如何实现类似SQL的操作?,list,haskell,vector,List,Haskell,Vector,我试图用Haskell做一些类似SQL的操作,但我不知道要使用什么样的数据结构。我有三个不同的表格:客户、销售和订单。模式如下: 顾客 custid-整数(主键) 名称-字符串 例如: 1|Samson Bowman 2|Zelda Graves 3|Noah Hensley 4|Noelle Haynes 5|Paloma Deleon 1|3|20/3/2014 2|4|25/4/2014 3|5|17/7/2014 4|9|5/1/2014 5|5|9/6/2014 2|gum 4

我试图用Haskell做一些类似SQL的操作,但我不知道要使用什么样的数据结构。我有三个不同的表格:客户、销售和订单。模式如下:

顾客
  • custid
    -整数(主键)
  • 名称
    -字符串
例如:

1|Samson Bowman
2|Zelda Graves
3|Noah Hensley
4|Noelle Haynes
5|Paloma Deleon
1|3|20/3/2014
2|4|25/4/2014
3|5|17/7/2014
4|9|5/1/2014
5|5|9/6/2014
2|gum
4|sandals
3|pen
1|gum
2|pen
3|chips
1|pop
5|chips
销售额
  • orderid
    -整数(主键)
  • custid
    -整数
  • 日期
    -字符串
例如:

1|Samson Bowman
2|Zelda Graves
3|Noah Hensley
4|Noelle Haynes
5|Paloma Deleon
1|3|20/3/2014
2|4|25/4/2014
3|5|17/7/2014
4|9|5/1/2014
5|5|9/6/2014
2|gum
4|sandals
3|pen
1|gum
2|pen
3|chips
1|pop
5|chips
命令
  • orderid
    -整数
  • 项目
    -字符串
例如:

1|Samson Bowman
2|Zelda Graves
3|Noah Hensley
4|Noelle Haynes
5|Paloma Deleon
1|3|20/3/2014
2|4|25/4/2014
3|5|17/7/2014
4|9|5/1/2014
5|5|9/6/2014
2|gum
4|sandals
3|pen
1|gum
2|pen
3|chips
1|pop
5|chips
我想做的是将这三个表“合并”到一个新表中,新表的模式是:

Customername    Order#     Date       Items
Samson Bowman   17      20/3/2014 shoes, socks, milk
Samson Bowman   34      19/5/2014 gum, sandals, butter, pens, pencils
Paloma Deleon   41      6/1/2014  computer
…
所以是的,它非常像SQL。我知道SQL非常简单,但是如果没有SQL,而是使用内置的数据结构,如何实现这一点呢

文本打印错误 当我运行该函数时,它显示以下错误:

Couldn't match type `[Char]' with `Char'
    Expected type: Customer -> String
      Actual type: Customer -> [String]
    In the first argument of `map', namely `formatCustomer'
    In the second argument of `($)', namely `map formatCustomer result'

我认为concept的返回类型是[Customer],但是formatCustomer只使用Customer。这就是原因吗?

你们所有的关联都是一对多的,它们之间并不相互关联;它是严格的等级制度。客户有销售,销售有订单。考虑到这一点,您可能不会单独存储每一位信息,而是按实际情况分层存储。我可以将其放入如下数据类型:

data Customer = Customer { customerName :: String
                         , sales        :: [Sale]
                         } deriving (Eq, Read, Show)

data Sale = Sale { saleDate  :: Day
                 , soldItems :: [String]
                 } deriving (Eq, Read, Show)
这可能很容易从Haskell内部进行操作,而且,作为奖励,它很容易变成您想要结束的表格,因为它一开始就非常接近


但也许我误解了你的问题,你不仅仅要求最好的数据结构来保存它,而是如何从平面数据结构转换成这种结构。幸运的是,这很容易。既然一切都是关键的,我会构建一个新的模型并开始使用它来处理事情,甚至更好,用它同时完成这两项工作。更具体地说,假设您有以下数据结构:

data DBCustomer = DBCustomer { dbCustomerName :: String
                             , dbCustomerID   :: Int
                             } deriving (Eq, Read, Show)

data DBSale = DBSale { saleOrderID    :: Int
                     , saleCustomerID :: Int
                     , dbSaleDate     :: Day
                     } deriving (Eq, Read, Show)

data DBOrder = DBOrder { dbOrderID   :: Int
                       , dbOrderItem :: String
                       } deriving (Eq, Read, Show)
如果我想要一个类型为
[DBSale]->[DBOrder]->[Sale]
的函数,我可以很容易地编写它:

condense :: [DBSale] -> [DBOrder] -> [Sale]
condense dbSales dbOrders = flip map dbSales $ \dbSale ->
    Sale (dbSaleDate dbSale)
       $ fromMaybe [] (Map.lookup (saleOrderID dbSale) ordersByID) where
  ordersByID = Map.fromListWith (++) . flip map dbOrders
             $ \dbOrder -> (dbOrderID dbOrder, [dbOrderItem dbOrder])
在这里,我放弃了客户ID,因为在
Sale
中没有相应的插槽,但是您当然可以添加另一个
Map
并取出整个
customer
对象:

condense :: [DBCustomer] -> [DBSale] -> [DBOrder] -> [Customer]
condense dbCustomers dbSales dbOrders = flip map dbCustomers $ \dbCustomer ->
    Customer (dbCustomerName dbCustomer)
           $ lookupDef [] (dbCustomerID dbCustomer) salesByCustomerID where
  lookupDef :: (Ord k) => a -> k -> Map.Map k a -> a
  lookupDef def = (fromMaybe def .) . Map.lookup
  salesByCustomerID = Map.fromListWith (++) . flip map dbSales
                    $ \dbSale -> (saleCustomerID dbSale,
                                  [ Sale (dbSaleDate dbSale)
                                  $ lookupDef [] (saleOrderID dbSale)
                                              ordersByID])
  ordersByID = Map.fromListWith (++) . flip map dbOrders
             $ \dbOrder -> (dbOrderID dbOrder, [dbOrderItem dbOrder])

印刷 这应该相当容易。我们将使用它,因为它可以更容易地将内容放入列中。总的来说,结果中的每一行都是一个
Sale
。首先,我们可以尝试格式化一行:

formatSale :: Customer -> Sale -> String
formatSale customer sale = printf "%-16s%-8d%-10s%s"
                                  (customerName customer)
                                  (orderID sale)
                                  (show $ saleDate sale)
                                  (intercalate "," $ soldItems sale)
(实际上,我们放弃了订单ID;如果您想在输出中保留该ID,您必须将其添加到
Sale
数据结构中。)然后获取每个客户的行列表很容易:

formatCustomer :: Customer -> [String]
formatCustomer customer = map (formatSale customer) $ sales customer
然后,如果
customers
concurate
的输出,则为所有客户执行此操作并将其打印出来:

putStr . unlines $ concatMap formatCustomer customers

我也有一些类似的问题,我发现执行SQL连接操作的最佳方法是使用包中的
align
函数,并结合
Map
(其中键位于要连接的内容上)


align
的结果将为您提供
这些a b
的地图或列表,这些a b可以是a
a
,a
b
,也可以是两者。这很好。

我曾经写过关于使用箭头进行类似SQL的列表处理的文章:但map只适用于两个元素的元组,在saleDB中,我们有三个元素,如何处理它?ThanksI建议您阅读Yesod书中的章节。它不是SQL应用程序。所有数据都存储在简单的txt文件中。所以我想使用简单的数据结构来收集它们,并使之变得简单:)谢谢!起初,我并没有注意到我可以创建自己的数据,只是想用元组列表来创建。它可能只适用于两个元素,但我的销售数据库有三个元素。你能提供更多的细节吗?因为我想看看如何连接三个数据structure@user3889372当前位置我编辑了我的答案,以展示加入这三者的效果。我注意到,在对您的问题的评论中,您说您这样做是为了数据持久性,如果您只想快速存储内容,而不需要与任何特定格式兼容,您可以使用
read
show
,如果您的数据类型派生
read
show
。然后您的文件将看起来像Haskell文字。谢谢!我试过你的代码,但出了点小错误。上面写着“不在范围内:'销售'”,在这里:$\dbSale->(saleCustomerID销售,[销售(dbSaleDate dbSale)$lookupDef[](saleOrderID dbSale)ordersByID])。在你回复之前,我会尝试修复它。顺便说一下,我在谷歌上搜索了“翻转地图”和map.map,但只找到了map.map。这里的地图和地图有什么区别?我是哈斯克尔的新人,非常感谢你的帮助@用户3889372:很抱歉
销售
-这应该是
dbSale
。在搜索Haskell文档时,您通常会发现它比Google更好。from
Data.Map
是一种将键“映射”到值的数据结构。是一个函数,用于转换列表中的每个元素。交换函数的参数。