我的一个工作职责是负责大型通讯订阅数据库的采矿和营销。我的每个时事通讯都有四列(newsletter_status,newsletter_datejoined,newsletter_dateunsub和newsletter_unsubmid)。
除了这些列之外,我还有一个主要的unsub列,我们的客户服务部门。可以更新以容纳希望从我们的所有邮件中删除的愤怒订阅者,以及在发生名为emailaddress_status的硬弹跳(或一定数量的软反弹)时更新的另一列。
当我为一个列表提取当前有效订阅者的计数时,我使用以下语法:
select count (*) from subscriber_db
WHERE (emailaddress_status = 'VALID' OR emailaddress_status IS NULL)
AND newsletter_status = 'Y'
and unsub = 'N' and newsletter_datejoined >= '2013-01-01';
我想要的是一个查询,查找包含%_status的所有列,上述条件按当前计数大小排序。
我希望它看起来像这样:
等
我几个月来一直在网上搜索类似的内容,但除了在终端中运行它们并导出结果之外,我还无法在一个查询中成功获取它们。
我正在运行PostgreSQL 9.2.3。
正确的测试用例是每个聚合总数与运行单个查询时得到的计数相匹配。
这是我对于序数位置{column>,char_limit和is_nullable'的无效table definition。
答案 0 :(得分:3)
你的架构绝对令人恐怖:
24 ***_status text YES
25 ***_status text YES
26 ***_status text YES
27 ***_status text YES
28 ***_status text YES
29 ***_status text YES
我认为掩盖的***
类似于出版物/新闻稿等的名称。
您需要阅读有关data normalization的内容,否则您将遇到一个问题,该问题会持续增长,直至您点击PostgreSQL's row-size limit。
由于每个感兴趣的项目都在不同的列中,因此使用现有模式解决此问题的唯一方法是使用PL / PgSQL EXECUTE format(...) USING ...
编写动态SQL。您可能只会将此视为临时选项,但它有点像使用打桩机将方柱塞入圆孔中,因为锤子不够大。
SQL中没有列名通配符,例如*_status
或%_status
。列是行的固定组件,具有不同的类型和含义。每当你发现自己希望这样的事情发生时,你的设计需要重新思考。
我不打算写一个例子,因为(a)这是一家电子邮件营销公司,(b)"混淆"架构完全无法用于任何类型的测试而无需重写大量的手动工作。 (将来,请为您的虚拟数据提供CREATE TABLE
和INSERT
语句,或者更好的是http://sqlfiddle.com/)。您将在PL / PgSQL中找到许多动态SQL的示例 - 以及如何通过正确使用format
来避免产生SQL注入风险的警告 - 快速搜索Stack Overflow。我过去写过一堆。
请,为了您的理智以及其他任何人需要在此系统上工作的理智,normalize your schema。
您可以通过规范化表格create a view来呈现旧结构,从而为您提供适应应用程序的时间。通过更多工作,您甚至可以定义DO INSTEAD
视图触发器(较新的Pg版本)或RULE
(较旧的Pg版本)以使视图可更新和可插入,因此您的应用甚至无法实现告诉我发生了任何变化 - 尽管这会带来性能成本,因此如果可能的话,最好调整应用程序。
从这样的事情开始:
CREATE TABLE subscriber (
id serial primary key,
email_address text not null,
-- please read http://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/
-- for why I merged "fname" and "lname" into one field:
realname text,
-- Store birth month/year as a "date" with a "CHECK" constraint forcing it to be the 1st day
-- of the month. Much easier to work with.
birthmonth date,
CONSTRAINT birthmonth_must_be_day_1 CHECK ( extract(day from birthmonth) = 1),
postcode text,
-- Congratulations! You made "gender" a "text" field to start with, you avoided
-- one of the most common mistakes in schema design, the boolean/binary gender
-- field!
gender text,
-- What's MSO? Should have a COMMENT ON...
mso text,
source text,
-- Maintain these with a trigger. If you want modified to update when any child record
-- changes you can do that with triggers on subscription and reducedfreq_subscription.
created_on timestamp not null default current_timestamp,
last_modified timestamp not null,
-- Use the native PostgreSQL UUID type, after running CREATE EXTENSION "uuid-ossp";
uuid uuid not null,
uuid2 uuid not null,
brand text,
-- etc etc
);
CREATE TABLE reducedfreq_subscription (
id serial primary key,
subscriber_id integer not null references subscriber(id),
-- Suspect this was just a boolean stored as text in your schema, in which case
-- delete it.
reducedfreqsub text,
reducedfreqpref text,
-- plural, might be a comma list? Should be in sub-table ("join table")
-- if so, but without sample data can only guess.
reducedfreqtopics text,
-- date can be NOT NULL since the row won't exist unless they joined
reducedfreq_datejoined date not null,
reducedfreq_dateunsub date
);
CREATE TABLE subscription (
id serial primary key,
subscriber_id integer not null references subscriber(id),
sub_name text not null,
status text not null,
datejoined date not null,
dateunsub date
);
CREATE TABLE subscriber_activity (
last_click timestamptz,
last_open timestamptz,
last_hardbounce timestamptz,
last_softbounce timestamptz,
last_successful_mailing timestamptz
);
答案 1 :(得分:0)
称它为“恐怖”,表明你很有机智和善意。谢谢。 :)我最近才继承这个架构(最初由StrongMail的人员创建)。
我今年的路线图上有一个完整的关系数据库重新建立项目 - 样本规范化非常符合我的工作。关于realname的非常有趣的见解,我没有真正想过这个。我认为StrongMail打破它的唯一原因是名字电子邮件个性化。
MSO是多系统运营商(有线电视公司)。我们是一家大型生活时尚媒体公司,我们制作的新闻通讯涉及食品,旅游,家居和园艺。
我正在为此创造一个小提琴 - 我是新来的,所以今后我会更加注意你们需要帮助的人。谢谢!