简体   繁体   English

MySQL 不返回相同的结果

[英]MySQL to not return same results

I have a table with list of customers:我有一张包含客户列表的表格:

customer

c_id    c_name      c_email     c_role
1       abc1        a1@abc.com  Dev
2       abc2        a2@abc.com  Dev
3       abc3        a3@abc.com  Dev
4       abc4        a4@abc.com  Dev
5       abc5        a5@abc.com  Dev
6       abc6        a6@abc.com  Dev
7       abc7        a7@abc.com  Dev
8       abc8        a8@abc.com  Dev
9       abc9        a9@abc.com  Dev

I query the table in the following way:我通过以下方式查询表:

select * from customer where c_role = 'Dev' order by c_id limit 2;

So, I get the results with:所以,我得到的结果是:

c_id    c_name      c_email     c_role
1       abc1        a1@abc.com  Dev
2       abc2        a2@abc.com  Dev

The business requirements says that if any records are accessed by a set of users for last 3 days, then those should not return in the subsequent query output.业务需求说,如果一组用户最近 3 天访问了任何记录,那么这些记录不应在后续查询 output 中返回。

So, if the user runs a query again for the next 3 days:因此,如果用户在接下来的 3 天内再次运行查询:

select * from customer where c_role = 'Dev' order by c_id limit 2;

The result should be:结果应该是:

c_id    c_name      c_email     c_role
3       abc3        a3@abc.com  Dev
4       abc4        a4@abc.com  Dev

Can anyone help me how to create this kind of rule in MySQL?谁能帮助我如何在 MySQL 中创建这种规则?

Adding a new column in current table is not going to help you.在当前表中添加新列对您没有帮助。

You will have to create another table where you store all c_ids a user has accessed and the datetime when the query was executed.您必须创建另一个表来存储用户访问过的所有 c_id 以及执行查询的日期时间。

CREATE TABLE IF NOT EXISTS `access_record` (
    `id` INT(11) NOT NULL AUTO_INCREMENT ,
    `c_id` INT(11) NOT NULL , // id of the record which user accessed
    `user_id` INT(11) NOT NULL , // id of the user who accessed the record
    `accessed_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
    PRIMARY KEY (`id`)
);

So whenever the user runs the next query you can use this table to know if user has already accessed a record or not and then use those c_ids to exclude them from next result set.因此,每当用户运行下一个查询时,您都可以使用此表来了解用户是否已经访问过记录,然后使用这些 c_ids 将它们从下一个结果集中排除。

SELECT
    c.c_id, c.c_role,c.c_name,c.c_email
FROM
    customer AS c
WHERE
    c.c_role = 'Dev'
    AND c.c_id NOT IN (
        SELECT
            ar.c_id
        FROM
            access_record AS ar
        WHERE ar.user_id = 1  // ofcourse this will change with each user (current user in your case I assume)
            AND ar.accessed_at > DATE_SUB(NOW(), INTERVAL 3 DAY)
     )
     ORDER BY c.c_id LIMIT 2;

This will give you all records which were not accessed by specific user within last 3 days.这将为您提供过去 3 天内特定用户未访问的所有记录。

I hope this helps.我希望这有帮助。

Answering @dang's question in comment在评论中回答@dang 的问题

How do I populate access_record when a query runs?查询运行时如何填充 access_record?

When you have fetched all the records then you extract c_ids from those records then you insert those c_ids into the access_record table.获取所有记录后,从这些记录中提取 c_ids,然后将这些 c_ids 插入access_record表中。

In MYSQL this query should do the trick在 MYSQL 这个查询应该可以解决问题

INSERT INTO access_record (c_id,user_id)

SELECT
    c.c_id, 1 // user_id of the user who is fetching records
FROM
    customer AS c
WHERE
    c.c_role = 'Dev'
    AND c.c_id NOT IN (
        SELECT
            ar.c_id
        FROM
            access_record AS ar
        WHERE ar.user_id = 1  // ofcourse this will change with each user (current user in your case I assume)
            AND ar.accessed_at > DATE_SUB(NOW(), INTERVAL 3 DAY)
     )
     ORDER BY c.c_id LIMIT 2;

You can also fetch those c_ids with one query then use second query to insert those c_ids into the access_record table.您还可以使用一个查询获取这些 c_id,然后使用第二个查询将这些 c_id 插入到 access_record 表中。

If you have all your records fetched in $records then如果您在$records中获取了所有记录,那么

$c_ids = array_column($temp, 'c_id'); // get all c_ids from fetched record array

Now run a query to insert all those c_ids.现在运行查询以插入所有这些 c_id。

I would add an extra table with users and accessdate.我会添加一个带有用户和访问日期的额外表格。 And make the business logic update those on access.并使业务逻辑更新那些访问。 For example:例如:

user |用户 | accessdate |访问日期 | c_id c_id

Your 'customer' table is data about customers.您的“客户”表是有关客户的数据。 That is all it should have.这就是它应该拥有的一切。

However, your selection criteria is really not what it appears to be .但是,您的选择标准确实不是它看起来的那样 What business requirements want you to implement is a feed, or pipeline, with the selection acting as a consumer, being fed by un-accessed customers.您希望实现的业务需求是提要或管道,选择充当消费者,由未访问的客户提供。

Each user (or group of users, ie: 'set of users') needs it's own feed, but that can be managed by a single table with a distinguishing field.每个用户(或用户组,即:“用户集”)都需要它自己的提要,但可以由具有区分字段的单个表管理。 So we need a user_group table to group your 'set of users'.因此,我们需要一个 user_group 表来对您的“用户集”进行分组。

// user_group
g_id    g_data
201     abc1
202     abc2
203     abc3

We will need to populate customer_feed with the access timestamps for each customer.我们需要使用每个客户的访问时间戳填充 customer_feed。 We can add a foreign keys to delete customers and user_groups when they are deleted, but we will need update the customer feed when we use it.我们可以添加一个外键来删除客户和 user_groups 被删除时,但我们需要在使用时更新客户提要。

create table customer_feed (
  c_id int(11) not null,
  g_id int(11) not null,
  at timestamp not null,
  primary key (c_id, g_id),
  constraint customer_fk foreign key (c_id) references customer on delete cascade
  constraint user_group_fk foreign key (g_id) references user_group on delete cascade,
);

// customer_feed
c_id    g_id   at
101     201    2018-11-26 07:40:21
102     201    2018-11-26 07:40:21
103     201    2018-11-26 07:40:22

When we want to read the customer data, we must do three queries:当我们要读取客户数据时,我们必须做三个查询:

  1. Update the feed for the current user-group.更新当前用户组的提要。
  2. Get the users from the feed从提要中获取用户
  3. Mark the users in the feed as consumed.将提要中的用户标记为已使用。

So, let's say we are using user_group 201.所以,假设我们正在使用 user_group 201。

When we update the feed, any new users to the feed are available to be read straight away,we give them a timestamp which is very early.当我们更新提要时,提要的任何新用户都可以立即阅读,我们给他们一个很早的时间戳。 So we can commemorate the battle of hastings...所以我们可以纪念黑斯廷斯之战...

// 1. update the customer_feed for user_group 201
insert into customer_feed(select c.c_id,201,TIMESTAMP('1066-10-14')
from customer c left join customer_feed f 
on c.c_id = f.c_id and f.g_id=201 where f.c_id is null);

we select from the customer and the feed... We only accept records whose access dates are less than three days ago.我们来自客户和提要的 select... 我们只接受访问日期少于三天前的记录。 This is the query you originally had, but with the feed restrictions.这是您最初的查询,但有提要限制。

// 2. read from feed for user_group 201
select c.* from customer c,customer_feed f where c.c_role='Dev'
and f.g_id=201 and f.c_id=c.c_id 
and f.at < date_sub(now(), interval 3 day) limit 2

..and now we need to mark the values from the feed as being consumed. ..现在我们需要将提要中的值标记为已消耗。 So, we gather the c_id we have selected into a list of c_id, eg: '102,103', and we mark them as consumed.因此,我们将选择的 c_id 收集到 c_id 列表中,例如:'102,103',并将它们标记为已使用。

// 3. Mark the feed as consumed for user_group 201
update customer_feed set at=now() where g_id=201 and c_id in '102,103'

Add a new column in your customer table like start_accessing and then you can run the query:在您的客户表中添加一个新列,例如 start_accessing,然后您可以运行查询:

SELECT * 
    FROM   customer 
    WHERE  c_role = 'Dev' 
           AND Date_add(start_accessing, INTERVAL 3 day) >= Curdate() 
    ORDER  BY c_id 
    LIMIT  2; 

start_accessing will be the column that will save when the user started accessing the resource. start_accessing 将是用户开始访问资源时将保存的列。

Add a datetime stamp to the table and query from that.向表中添加日期时间戳并从中查询。

There might be a way to get a 3 day rotation without having to change the tables.可能有一种方法可以进行 3 天的轮换,而无需更改表格。

By calculating batches of devs.通过计算批次的开发人员。
And calculate the current dev batch based on the current date.并根据当前日期计算当前开发批次。

The example below is for MySql 7.x (no window functions)以下示例适用于 MySql 7.x(无 window 功能)

set @date = current_date;
-- set @date = date('2020-07-04');

set @dayslimit = 3;
set @grouplimit = 2;
set @devcnt = (select count(*) cnt 
       from test_customer 
        where c_role = 'Dev');
set @curr_rnk = ((floor(datediff(@date, date('2000-01-01')) / @dayslimit)%floor(@devcnt / @dayslimit))+1);

select c_id, c_name, c_email, c_role
-- , rnk
from
(
   select t.*, 
    case when @rn := @rn +1 then @rnk := ceil((@rn/@grouplimit)%(@devcnt+1/@grouplimit)) end as rnk
   from test_customer t
   cross join (select @rn:=0, @rnk:= null) vars
   where c_role = 'Dev'
   order by c_id
) q
where rnk = @curr_rnk
order by c_id
limit 2;

A test on rextester here对 reextester 的测试在这里

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM