SQL:List-Field包含子列表

时间:2015-09-08 12:36:56

标签: sql haskell persistence persistent esqueleto

快速前言:我使用SQL实现persistent(Haskell)和esqueleto

无论如何,我想要一个带有ID Category 0 ["math", "algebra"] 1 ["personal", "life"] 2 ["algebra", "university", "personal"] 类型列的SQL表,即一个字符串列表。现在我想创建一个查询,它给出了所有记录,其中给定列表是记录中的列表的子列表。

例如带

的表格
["personal", "algebra"]
查询["personal", "algebra"]

将仅返回ID = 2的记录,因为["algebra", "university", "personal"]ourBrow.setWebViewClient(new WebViewClient() { @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { Log.d("WEB_VIEW_TEST", "error code:" + errorCode + " - " + description); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // handle different requests for different type of files // this example handles downloads requests for .apk and .mp3 files // everything else the webview can handle normally if (url.endsWith(".apk")) { Uri source = Uri.parse(url); // Make a new request pointing to the .apk url DownloadManager.Request request = new DownloadManager.Request(source); // appears the same in Notification bar while downloading request.setDescription("Description for the DownloadManager Bar"); request.setTitle("YourApp.apk"); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { request.allowScanningByMediaScanner(); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); } // save the file in the "Downloads" folder of SDCARD request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "SmartPigs.apk"); // get download service and enqueue file DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE); manager.enqueue(request); } else if(url.endsWith(".mp3")) { // if the link points to an .mp3 resource do something else } // if there is a link to anything else than .apk or .mp3 load the URL in the webview else view.loadUrl(url); return true; } }); 的子列表。

这样的查询是否可以使用我所追求的子列表和“基本”SQL运算符的可变长度?

如果有人知道他们在持久性/ esqueleto周围的方式当然会很棒。

感谢。

2 个答案:

答案 0 :(得分:3)

扩展Gordon Linoff的评论和之前的回答:

SQL数据库有时会受到限制。由于[String]中字符串的顺序似乎并不重要,因此您尝试将类似set的内容放入关系数据库中,并针对您的查询建议类似is a subset of运算符的内容

如果有一个提供这些结构的数据库引擎,使用它没有任何问题(我不知道)。但是,近似您的设置逻辑(或数据库本身不支持的任何逻辑)都有缺点:

  • 你必须明确处理边缘情况(参见xnyhps'回答)
  • 您需要在代码中明确处理它,而不是隐藏存储数据的复杂性
  • 您需要学习数据库引擎而不是编写Haskell代码
  • 数据库和Haskell代码之间的接口变得模糊

更强大的方法是将您的存储任务重新制定为适合关系数据库概念的东西。即试着把它放在关系方面。 实体和关系很简单,因此可以避免边缘情况。您不需要打扰如何数据库后端存储您的数据。您根本不必费心去理解数据库。并且您的界面简化为相当简单的查询(使用连接)。通过查询无法(相对)轻松实现的所有内容(可能)都属于Haskell代码。

当然,细节因具体情况而异。

在您的具体情况下,您可以使用以下内容:

Table: Category
ID Description
0  math
1  algebra
2  personal
3  life
4  university

Table: CategoryGroup
ID  CategoryID
0   0
0   1
1   2
1   3
2   1
2   4
2   2

...外键关系允许拥有类别组。在这里,您正在使用它擅长的关系数据库。为了查询CategoryGroup,您将加入两个表,从而产生类型

的结果
[(Entity CategoryGroup, Entity Category)]

我将在Haskell中转换为类似

的内容
[(Entity CategoryGroup, [Entity Category])]

为每个CategoryCategoryGroup - 模型中需要deriving (Eq, Ord))收集CategoryGroup个实体的位置。

对于给定的列表cs :: [Entity Category],如上所述的集合逻辑将类似于

import qualified Data.Set as Set
import Data.Set (isSubsetOf)
let s = Set.fromList ["personal", "algebra"]
let s0 = Set.fromList $ map (categoryDescription . entityVal) cs
if s `isSubsetOf` s0 -- ... ?

习惯于关系数据库的限制一开始可能很烦人。我想,对于一些具有重要意义的东西(持久数据),一个强大的概念通常比一个强大的概念更好,并且总是知道你的数据库正在做什么完全

答案 1 :(得分:1)

通过使用[String],持久性将整个列表转换为带引号的字符串,这使得使用SQL非常困难。

您可以执行以下操作:

mapM (\cat ->
         where_ (x ^. Category `like` (%) ++. val (show cat) ++. (%)))
     ["personal", "algebra"]

但这非常脆弱(当类别包含"等时可能会中断。)。

更好的方法是:

  1. 如果数据库足够小,你可以在Haskell中进行过滤。

  2. 将数据建模为:

    物件:

    ID  ... 
    0   ...
    1   ...
    2   ...
    

    ObjectCategories:

    ObjectID  Category
    0   math
    0   algebra
    1   personal
    1   life
    2   algebra
    2   university
    2   personal