最近,在《第二届监视器》中,关于自拍照答案的简短讨论开始于此帖子。
有一次,一个用户说:
我不知道有多少自拍照:s
我立刻想到:这是SEDE查询的绝佳机会!
试试看!
DECLARE @QuestionId INT = (SELECT Id FROM PostTypes WHERE Name = 'Question');
DECLARE @AnswerId INT = (SELECT Id FROM PostTypes WHERE Name = 'Answer');
DECLARE @UserAnswers TABLE (AnswerId INT, ParentId INT);
DECLARE @UserQuestions TABLE (QuestionId INT);
INSERT INTO @UserAnswers
SELECT Id, ParentId
FROM Posts
WHERE OwnerUserId = ##UserId## AND PostTypeId = @AnswerId;
INSERT INTO @UserQuestions
SELECT Id
FROM Posts
WHERE OwnerUserId = ##UserId## And PostTypeId = @QuestionId;
SELECT AnswerId
FROM @UserAnswers
JOIN @UserQuestions
ON ParentId=[@UserQuestions].QuestionId;
以上查询获取用户ID并返回他们发布的所有自拍照答案的列表。
这是我第一次编写任何形式的SQL,但我仍在努力适应它的总体流程,所以我真的很欢迎任何建议。
但是,我想到了以下几点:
将帖子存储在表中是否很好,或者
我遵循良好的SQL惯例/惯例吗?
我对如何将SQL分成多行感到非常困惑,因此,经过大量的试验和错误,在我看来,每行只有一条指令是最干净的。但是,如果不建议这样做,我可以改变。
我是否在任何地方都缺少缩进?
对我来说,阅读SQL就像阅读英语一样。因此,编写文档似乎是多余的。但是,我不熟悉好的文档和SQL,因此我应该添加一些文档方面的知识吗?
#1 楼
然后,一个接一个地:将帖子存储在表中是好事还是没有必要?
没必要,在这种情况下。您的表变量在这里为您做什么实际上是在模拟一个自引用
join
。您可以在实际的表上使用别名,例如:FROM Posts AS "Answers"
JOIN Posts AS "Questions"
ON "Answers".OwnerUserId = "Questions".OwnerUserId
AND "Answers".ParentId = "Questions".Id
AND "Answers".PostTypeId = @AnswerId
AND "Questions".PostTypeId = @QuestionId
WHERE "Answers".OwnerUserId = ##UserId##
AND "Questions".OwnerUserId = ##UserId##
(请注意,别名不需要双引号,但是我个人喜欢使用它们作为视觉参考,即某些东西是别名而不是实际的数据库对象)
我是否遵循良好的SQL惯例/惯例?
是的,我认为看起来不错(除了我刚才提到的内容)。
我对如何将SQL分成多行感到非常困惑因此,经过反复尝试,在我看来每行只有一条指令是最干净的。但是,如果不建议这样做,我可以改变。
......
我在任何地方都缺少缩进吗?
在SQL中,缩进/格式化样式与编写SQL的人一样多。对许多人来说似乎有效的一种方法是让所有主关键字都最小缩进,其余缩进2、4或更多空格,无论需要什么。
主要关键字是(用于
SELECT
语句):WITH
SELECT
FROM
WHERE
GROUP BY
HAVING
ORDER BY
当然很多时候您不会使用所有这些关键字,但是作为一个经验法则,效果很好。对于其余的内容,只要您保持可读性并使用一些常识即可申请其他语言,则应该做得很好。
更新:解决其他问题。
对我来说,阅读SQL就像阅读英语一样。因此,编写文档似乎是多余的。但是,我不熟悉好的文档和SQL,因此我应该添加一些文档方面的知识吗?
像往常一样,答案取决于它。在像您这样的简单查询中,文档可能会很混乱。与其他所有语言一样,在SQL中,如果代码编写得很好(尤其是良好的命名,别名等),则通常不需要文档。
我想出了一种样式的文档,它对于查询可能需要其他人照顾的查询很有用。这是一个示例(假设您正在某个时间段内查询数据,也可以通过要求用户输入一个时间段来使用SEDE进行查询):
DECLARE /* The interval in Days how far back we want to query for values. */
@DaysInterval INT = 90;
IF @DaysInterval > 0 /* meaning we would go into the future instead of the past... */
SET @DaysInterval = @DaysInterval * -1;
DECLARE /* Variables for which dates to query from and up to, based on @DaysInterval. */
@FromDate DATETIME = DATEADD(DAY, @DaysInterval, GETDATE())
, @ToDate DATETIME = GETDATE();
DECLARE /** Filters for which tags to query. */
@TagsFilter TABLE (
Tag VARCHAR(20)
);
INSERT INTO @TagsFilter
SELECT 'sql' UNION
SELECT 'python' UNION
-- etc.
我利用了SQL通常读起来像英语这样的事实,来添加与之类似的文档。
在某些情况下,您想添加说明为什么要执行某些操作的文档方式,因为它现在一直很明显...例如:
/**
* Temp table to hold results from the posts tables from each respective SE site database.
* This will be used in conjunction with @TagsFilter in order to match records.
*/
IF OBJECT_ID('tempdb..#SESites') IS NOT NULL
DROP TABLE #SESites;
CREATE TABLE #SESites (
-- etc.
评论
\ $ \ begingroup \ $
关于“我利用SQL经常读得像英语这样的事实来添加与之一起使用的文档这一事实非常高兴。”我想我很久以前就尝试使用JavaScript进行类似的操作。
\ $ \ endgroup \ $
– SirPython
15年8月31日在22:13
\ $ \ begingroup \ $
不知道使用JS多么容易,但是毕竟我们拥有JSDoc;)
\ $ \ endgroup \ $
– ran
15年8月31日在22:49
\ $ \ begingroup \ $
为什么同时打开“ Answers” .OwnerUserId =“ Questions” .OwnerUserId和WHERE“ Answers” .OwnerUserId = ## UserId ##和“ Questions” .OwnerUserId = ## UserId ##?如果冗余旨在帮助查询优化器,则最好对此进行解释,并理想地链接到确认其有所作为的资源。
\ $ \ endgroup \ $
– Peter Taylor
2015年9月1日9:41
\ $ \ begingroup \ $
@PeterTaylor实际上是复制粘贴原始查询的结果。我不确定查询优化器将如何以一种或另一种方式进行处理,在这种情况下,即使很小也没有什么不同。
\ $ \ endgroup \ $
– ran
2015年9月1日于14:31
\ $ \ begingroup \ $
从逻辑上讲,JOIN仅在Answer.ParentId == Question.Id之上,并且已经在Answer.PostTypeId == @AnswerType等之上,而已成为WHERE子句的一部分。这还仅仅是复制粘贴问题还是有技术优势?
\ $ \ endgroup \ $
–哈根·冯·埃森(Hagen von Eitzen)
2015年9月1日,16:10
#2 楼
这可以通过更简单的联接来解决。将数据加载到表变量中的过程实际上使过程变得复杂得多。请考虑以下内容:
select Q.OwnerUserID as [User Link], Q.Id as [Post Link]
from Posts Q
inner join Posts A
on A.parentId = Q.Id
and A.OwnerUserID = Q.OwnerUserID
and A.PostTypeId = 2
where Q.OwnerUserID = ##UserId##
and Q.PostTypeId = 1
在此处查看查询:我的自拍照
使用SQL,始终保持基于Set的心态/框架几乎总是更好。
评论
\ $ \ begingroup \ $
选择Q.OwnerUserID作为[用户链接],然后选择Q.OwnerUserID = ## UserId ## ...,因为用户可能忘记了输入的ID ...?
\ $ \ endgroup \ $
–西蒙·福斯伯格
15年8月31日在21:24
\ $ \ begingroup \ $
@Simon-因为用户喜欢自我验证,所以自我将您的名字视为链接对我很重要!
\ $ \ endgroup \ $
–rolfl
15年8月31日在21:25
\ $ \ begingroup \ $
问从哪里来?
\ $ \ endgroup \ $
– SirPython
15年8月31日在21:42
\ $ \ begingroup \ $
好吧,坦白,我喜欢Q和A别名,用于与SEDE中的“问答”相关的查询。 Q被创建为PostTypeId = 1的帖子的别名-换句话说,是Questions。在此处创建此别名:来自帖子Q。
\ $ \ endgroup \ $
–rolfl
15年8月31日在21:43
#3 楼
我将帖子存储在表中是好事还是不必要?
我会说这是不必要的。
使用这些临时表,可以直接在
SELECT
表上运行带有JOIN
的Posts
。另外,使用
ParentId
条件,不需要@QuestionId
和@AnswerId
。 (最好将其命名为Type
而不是IMO BTW Id
)。仅答案设置了ParentId
,并且仅回答了一个问题。还应该使用SEDE专有技巧为列
Post Link
命名,以使其(您猜对了)发布链接。此外,您可以在查询中添加更多有趣的列,例如答案得分,问题得分和创建日期。
您的SEDE查询可以重写如下:
SELECT Answers.Id AS [Post Link],
Answers.Score AS AnswerScore,
Questions.Score AS QuestionScore,
Questions.CreationDate
FROM Posts AS Answers
JOIN Posts AS Questions ON (Answers.ParentId = Questions.Id)
WHERE Answers.OwnerUserId = ##UserId##
AND Questions.OwnerUserId = ##UserId##;
在此处进行测试:https://data.stackexchange.com/codereview/query/357962/selfies?UserId=31562
评论
\ $ \ begingroup \ $
为什么上述查询未获取我的自拍照之一?
\ $ \ endgroup \ $
–宝山
2015年9月1日7:45
\ $ \ begingroup \ $
@Bhushan,因为它在堆栈溢出而不是代码审查中
\ $ \ endgroup \ $
– Johnbot
2015年9月1日在8:18
评论
这个查询很棒。我只是浏览了我所有的自拍照,发现它们不仅传达了我工作和爱好项目的非常不错的历史,而且还是我面临的一些最有趣和令人困惑的问题。SQL就像阅读英文一样,告诉我必须维护的4K LOC程序。 XD