关于连接和限制的复杂SQL查询

时间:2010-12-29 02:11:15

标签: sql mysql

SELECT DISTINCT users.id as expert_id, users.firstname, users.lastname
    , projects.id as project_id, projects.project_title
    , projects.project_budget, projects.created as project_created
FROM USERS
RIGHT JOIN expert_skills ON expert_skills.expert_id = users.id
JOIN project_skills ON project_skills.skill_id = expert_skills.skill_id
JOIN projects ON projects.id = project_skills.project_id
WHERE projects.status = 1

此查询为我提供了与用户相关的不同项目,但我想限制每位专家的项目数。例如,我想要与专家相关的项目,但项目数量最多可达10个。我的查询需要一个限制工具。我怎么能这样做?谢谢

11 个答案:

答案 0 :(得分:1)

您可以将此技术应用于您的问题:OLAP functions

只需将选择限制为等级< = 10

答案 1 :(得分:0)

如果MySQL有row_number,这将非常容易,但事实并非如此。

因此,您需要将Andomar的answer修改为类似的问题。

 set @num := 0, @projectid:= -1;


 SELECT DISTINCT users.id as expert_id, users.firstname, users.lastname, projects.id as                project_id, projects.project_title, projects.project_budget, projects.created as project_created
 FROM 
     USERS
        RIGHT JOIN expert_skills ON expert_skills.expert_id = users.id
        JOIN project_skills ON project_skills.skill_id = expert_skills.skill_id
        JOIN projects ON projects.id = project_skills.project_id
    JOIN
    (select  
            users.id uid,
           project.id pid,
           @num := if(@projectid= projects.id, @num + 1, 1) 
                as row_number,
            @projectid:= project.id
    from    USERS
        RIGHT JOIN expert_skills ON expert_skills.expert_id = users.id
        JOIN project_skills ON project_skills.skill_id = expert_skills.skill_id
        JOIN projects ON projects.id = project_skills.project_id
    WHERE projects.status = 1
    order by 
            users.id, project.id desc
    ) as projectNum 
    on users.id = pid
    and projects.id = pid
   where   projectNum.row_number <= 10

data.stackoverflow.com中查看此查询,了解如何在支持窗口的数据库中执行此操作

答案 2 :(得分:0)

使用rownum进行subselect限制应该可以正常工作

SELECT DISTINCT users.id as expert_id, users.firstname, users.lastname, projects.id as project_id, projects.project_title, projects.project_budget, projects.created as project_created
FROM USERS
RIGHT JOIN expert_skills ON expert_skills.expert_id = users.id
JOIN project_skills ON project_skills.skill_id = expert_skills.skill_id
JOIN (select * from (select a.project_id, additional_rows from projects) a where rownum < 11) projects_skills ON projects.id = project_skills.project_id
WHERE projects.status = 1

答案 3 :(得分:0)

可以使用stored proceduresin-memory tables和使用WHILE循环的控制流的组合来实现:

DELIMITER $$

DROP PROCEDURE IF EXISTS `Get10ProjectsPerUserByStatus`$$
-- 1. DEFINE STORED PROCEDURE
CREATE PROCEDURE `Get10ProjectsPerUserByStatus`(_status INT)
BEGIN
    -- 2. DECLARE VARIABLES AND IN-MEMORY TABLES
    DECLARE _id INT;
    DROP TABLE IF EXISTS temp_user;
    DROP TABLE IF EXISTS temp_project_user;
    CREATE TABLE temp_user (id INT) ENGINE=MEMORY;
    CREATE TABLE temp_project_user (p_id INT, u_id INT) ENGINE=MEMORY;

    -- 3. ADD ALL USERS AND LOOP BY REMOVING 1 USER AT A TIME
    INSERT INTO temp_user SELECT id FROM users;
    WHILE (SELECT COUNT(*) FROM temp_user) > 0 DO
        SET _id = (SELECT MIN(id) FROM temp_user);
        INSERT INTO temp_project_user
        SELECT ps.project_id, _id
        FROM project_skills ps 
        JOIN expert_skills es ON ps.skill_id = es.skill_id
        WHERE es.expert_id = _id
        LIMIT 10;
        DELETE FROM temp_user WHERE id = _id;
    END WHILE;

    -- 4. SELECT FROM IN-MEMORY TABLE AND JOIN TO EXISTING SCHEMA
    SELECT DISTINCT t.u_id AS expert_id,
    u.firstname, 
    u.lastname,
    t.p_id AS project_id, 
    p.project_title,
    p.project_budget, 
    p.created AS project_created
    FROM temp_project_user t
    INNER JOIN users u ON u.id = t.u_id
    INNER JOIN projects p ON p.id = t.p_id;

    -- 5. DROP IN-MEMORY TABLES
    DROP TABLE temp_user;
    DROP TABLE temp_project_user;
    END$$

DELIMITER ;

-- 6. CALL STORED PROCEDURE & DROP WHEN FINISHED
CALL Get10ProjectsPerUserByStatus(1);

DROP PROCEDURE IF EXISTS `Get10ProjectsPerUserByStatus`;

注意:超出此处的问题,但实际上可以使用相同的逻辑将10项目限制作为存储过程的参数应用:

-- GET 10 PROJECTS PER USER WITH STATUS = 1
CALL GetProjectsPerUserByStatus(1, 10) 

要执行此操作,您需要额外增加一个in-memory table和另一个WHILE循环

答案 4 :(得分:0)

这样的事情怎么样。

SELECT  DISTINCT 
  users.id as expert_id, 
  users.firstname, 
  users.lastname, 
  myProjects.project_id, 
  myProjects.project_title, 
  myProjects.project_budget, 
  myProjects.project_created
FROM USERS RIGHT JOIN 
  expert_skills ON expert_skills.expert_id = users.id JOIN 
  project_skills ON project_skills.skill_id = expert_skills.skill_id JOIN 
  (SELECT P1.id as project_id, 
    P1.project_title, 
    P1.project_budget, 
    P1.created as project_created  
  FROM projects AS P1
  WHERE   P1.status = 1 AND 
    (SELECT COUNT(*)
    FROM projects AS P2 
    WHERE P2.ID <= P1.ID) <= 10) AS myProjects ON myProjects.id = project_skills.project_id

答案 5 :(得分:0)

试试这个:

SELECT 
  users.id as expert_id, users.firstname,
  projects.id as project_id, projects.project_title,
  count(distinct users.id, ps2.project_id)
FROM USERS
  JOIN expert_skills ON expert_skills.expert_id = users.id
  JOIN project_skills ON project_skills.skill_id = expert_skills.skill_id
  JOIN projects ON projects.id = project_skills.project_id 
  JOIN expert_skills es2 ON es2.expert_id = users.id
  LEFT JOIN project_skills ps2 ON ps2.skill_id = es2.skill_id and ps2.project_id < projects.id
WHERE projects.status = 1
group by users.id, users.firstname, projects.id, projects.project_title
having count(distinct users.id, ps2.project_id) < 10
order by users.id, projects.id

假设10个项目的任何子集都有效,否则您将不得不调整标准。

为了清楚起见,我从选择中删除了一些字段。

答案 6 :(得分:0)

我认为你需要的是一个在内部有一个TOP 10的子查询。

像这样的东西(我真的没有得到查询,所以它可能不是你需要的)

SELECT users.id as expert_id, users.firstname, users.lastname, projs.*
  FROM Users
       RIGHT JOIN 
       (SELECT DISTINCT TOP 10 projects.id as project_id, projects.project_title, projects.project_budget, projects.created as project_created
       FROM expert_skills 
            right JOIN project_skills ON project_skills.skill_id = expert_skills.skill_id
            right JOIN projects ON projects.id = project_skills.project_id
      WHERE projects.status = 1 ) projs ON expert_skills.expert_id = users.id

答案 7 :(得分:0)

大多数数据库都有办法要求查询的前X行。在DB2中,您可以使用以下语句返回前10行。

select * from tableName fetch first 10 rows only;

我相信在 MySql 中您使用限制关键字。

从tableName限制10中选择*;

如果您只想要拥有少于或等于10个项目的人,那么这不会起作用。我不清楚你是想得到那些有10个或更少项目的人,或者你是否想要只返回前10个项目。

答案 8 :(得分:0)

我会反向开始...首先获得项目,然后找到那些技能并拥有这些技能,按照“专家”的顺序计数器。从该结果集中,只需应用where子句以排除超出“截止”的范围。通过使用@ mysql变量并使用ORDER BY子句,您可以按专家ID的顺序获得返回的结果,因此在设置它们时,请查看是否为同一专家。如果是,请在序列计数器中添加一个。如果不同,则设置为零...那么,更新最后一位专家在结果集中的下一条记录上进行比较的人。

SELECT
      PreQuery.expert_id, 
      PreQuery.firstname, 
      PreQuery.lastname, 
      PreQuery.Project_ID,
      PreQuery.Project_Title,
      PreQuery.Project_Budget,
      PreQuery.Project_Created,
      PreQuery.LastSeq
   from 
      ( SELECT DISTINCT
              U.id as expert_id, 
              U.firstname, 
              U.lastname, 
              P.ID as Project_ID,
              P.Project_Title,
              P.Project_Budget,
              P.Created as Project_Created,
              @Seq := if( @LastExpert = U.ID, @Seq +1, 1 ) LastSeq,
              @LastExpert := U.ID as IgnoreThis
           from
              Projects P
                 JOIN Project_Skills PS
                    ON P.ID = PS.Project_ID
                    JOIN Expert_Skills ES
                       ON PS.Skill_ID = ES.Skill_ID
                       JOIN Users U
                          ON ES.Expert_ID = U.ID,
              (select @Seq := 0, @LastExpert = 0 ) SQLVars   
           where
              P.Status = 1
           order by 
              U.ID ) PreQuery
   where
      PreQuery.LastSeq < 11

答案 9 :(得分:-1)

这个怎么样......

SELECT users.id as expert_id, users.firstname, users.lastname, projects.id as project_id, projects.project_title, projects.project_budget, projects.created as project_created
FROM USERS
RIGHT JOIN expert_skills ON expert_skills.expert_id = users.id
JOIN project_skills ON project_skills.skill_id = expert_skills.skill_id
JOIN projects ON projects.id = project_skills.project_id
WHERE projects.status = 1
GROUP BY users.id as expert_id, users.firstname, users.lastname, projects.id as project_id, projects.project_title, projects.project_budget, projects.created
HAVING COUNT(*) < 10

这假设您的原始查询完全符合您的要求,但您只想将结果限制为返回的行少于10的位置。

可能没有其他帖子那么高效,我可能有错误的结束,但值得我2美分:)

答案 10 :(得分:-1)

我建议使用DENSE_RANK。我没有你的表结构,所以我创建了自己的,用户和项目表,具有1对多的关系。因此,为用户选择最后2个项目的查询是这样的(注意,您可以将其放入存储过程并使用该数字作为输入参数):

declare @numberOfProjects int
set @numberOfProjects = 3

;with topNProjects(userid, projectid, createdtutc, dense_rank)
as (
    select p.userid, P.ProjectId, p.CreatedDtUtc, DENSE_RANK() OVER (PARTITION BY P.UserId ORDER BY P.ProjectId) AS DENSE_RANK
    from DS_Project P
)
select userid, projectid, createdtutc from topNProjects
where dense_rank <= @numberOfProjects
order by projectid desc

DENSE_RANK有效地返回按字段分组的当前1 + 1之前的数字记录。

已编辑:我被指出这是MySQL的问题,而不是MSSQL。我没有安装MySQL,但显然排名还没有(或曾经)。我做了一些关于排名的搜索,我发现这个SO帖子可能有助于回答原始问题:How to perform grouped ranking in MySQL。因此,一旦添加排名,逻辑保持不变 - 选择排名低于每个用户所需项目数的所有记录。