mysql Rank grouped function for a COUNT

1

I have 3 tables in MYSQL represented here as an example:

IneedaquerythatreturnsaRANKofcontractsexecutedgroupedbycommercialinagivenperiodoftime,forexample:

AquerythatreturnstheRANKofthecommercialsbetween2016-01-01and2016-01-03,shouldlooklikethis:

I have tried several solutions but none of them returns the result I need, for example an important point is when the commercial has the same number of contracts must have the same position in the ranking, in this example the commercial Com1 and Com2 have the position 1 in the Ranking because both have 2 contracts in the chosen period.

My query is currently:

    set @rn:=0,@grp:=0,@prevdate:='';
SELECT COUNT(*) AS NumContratos, NomeCom,NomeEmp
     , ( select count(*)+1
           from (
                select IdComercial
                     , count(*) as c
                  from contratos
                group
                    by IdComercial
                ) as m
          where c >
                (
                select count(*) 
                  from contratos as e

                 where e.IdComercial = T.IdComercial
                )
       ) AS Rank
     , T.IdComercial AS IdCom
  FROM contratos AS T

INNER JOIN comerciais ON T.IdComercial=comerciais.IdComercial
INNER JOIN empresas ON T.IdEmpresa=empresas.IdEmpresa

WHERE (DataContrato BETWEEN '2015-01-01' AND '2016-01-03')

GROUP 
    BY T.IdComercial
ORDER
    BY Rank

And this is the script for creating the database, table and data entry:

-- --------------------------------------------------------
-- Host:                         localhost
-- Server version:               10.1.9-MariaDB - mariadb.org binary distribution
-- Server OS:                    Win32
-- HeidiSQL Version:             9.2.0.4947
-- --------------------------------------------------------

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET NAMES utf8mb4 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;

-- Dumping database structure for teste
CREATE DATABASE IF NOT EXISTS 'teste' /*!40100 DEFAULT CHARACTER SET latin1 */;
USE 'teste';


-- Dumping structure for table teste.comerciais
CREATE TABLE IF NOT EXISTS 'comerciais' (
  'IdComercial' int(11) NOT NULL AUTO_INCREMENT,
  'IdEmpresa' int(11) NOT NULL DEFAULT '0',
  'NomeCom' varchar(100) NOT NULL DEFAULT '0',
  PRIMARY KEY ('IdComercial'),
  KEY 'IdEmpresa' ('IdEmpresa')
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;

-- Dumping data for table teste.comerciais: ~5 rows (approximately)
/*!40000 ALTER TABLE 'comerciais' DISABLE KEYS */;
INSERT INTO 'comerciais' ('IdComercial', 'IdEmpresa', 'NomeCom') VALUES
    (1, 1, 'Com1'),
    (2, 1, 'Com2'),
    (3, 2, 'Com3'),
    (4, 2, 'Com4'),
    (5, 4, 'Com5');
/*!40000 ALTER TABLE 'comerciais' ENABLE KEYS */;


-- Dumping structure for table teste.contratos
CREATE TABLE IF NOT EXISTS 'contratos' (
  'IdContratos' int(11) NOT NULL AUTO_INCREMENT,
  'IdComercial' int(11) NOT NULL,
  'DataContrato' date NOT NULL,
  PRIMARY KEY ('IdContratos'),
  KEY 'IdComercial' ('IdComercial')
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;

-- Dumping data for table teste.contratos: ~5 rows (approximately)
/*!40000 ALTER TABLE 'contratos' DISABLE KEYS */;
INSERT INTO 'contratos' ('IdContratos', 'IdComercial', 'DataContrato') VALUES
    (1, 1, '2016-01-01'),
    (2, 1, '2016-01-01'),
    (3, 2, '2016-01-02'),
    (4, 3, '2016-01-02'),
    (5, 4, '2016-01-03');
/*!40000 ALTER TABLE 'contratos' ENABLE KEYS */;


-- Dumping structure for table teste.empresas
CREATE TABLE IF NOT EXISTS 'empresas' (
  'IdEmpresa' int(11) NOT NULL AUTO_INCREMENT,
  'NomeEmp' varchar(100) DEFAULT NULL,
  PRIMARY KEY ('IdEmpresa')
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=latin1;

-- Dumping data for table teste.empresas: ~5 rows (approximately)
/*!40000 ALTER TABLE 'empresas' DISABLE KEYS */;
INSERT INTO 'empresas' ('IdEmpresa', 'NomeEmp') VALUES
    (1, 'Emp1'),
    (2, 'Emp2'),
    (3, 'Emp3'),
    (4, 'Emp4'),
    (5, 'Emp5');
/*!40000 ALTER TABLE 'empresas' ENABLE KEYS */;
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
/*!40014 SET FOREIGN_KEY_CHECKS=IF(@OLD_FOREIGN_KEY_CHECKS IS NULL, 1, @OLD_FOREIGN_KEY_CHECKS) */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;

What gives me back is not consistent with what I need, for example, it returns me a rank where for example the 6 position is missing (in case of my database)

    
asked by anonymous 04.12.2016 / 17:50

1 answer

3

Your query would look something like this:

SELECT x.IdComercial,
       x.NomeCom,
       emp.NomeEmp,
       CASE x.quantidade
           when @curQtd then @curRank
           else @curRank := @curRank + 1
       END AS Rank,
       CASE @curQtd
            when x.quantidade then @curQtd := quantidade
            else @curQtd := x.quantidade
       END AS NumContratos
  FROM (SELECT com2.IdComercial,
               com2.NomeCom,
               com2.IdEmpresa,
               COUNT(1) AS quantidade
         FROM comerciais com2
              INNER JOIN contratos con2 ON con2.IdComercial = com2.IdComercial
         GROUP BY com2.IdComercial) x
       INNER JOIN empresas emp ON x.IdEmpresa = emp.IdEmpresa
       JOIN (SELECT @curRank := 0, @curQtd := 0) r -- Aqui apenas zeramos os valores iniciais
 ORDER BY x.quantidade DESC

The last JOIN is to zero the variables and the first is to obtain the quantities. Note that the variables are incremented according to the rules of the case and are only changed if the quantity is changed according to the previous line.

If you want to show even commercials that do not have contracts, you should use LEFT JOIN that will still register, even if the other does not exist, and put the IdContratos column in the COUNT so the contract will have the correct summation:

SELECT x.IdComercial,
       x.NomeCom,
       emp.NomeEmp,
       CASE x.quantidade
           when @curQtd then @curRank
           else @curRank := @curRank + 1
       END AS Rank,
       CASE @curQtd
            when x.quantidade then @curQtd := quantidade
            else @curQtd := x.quantidade
       END AS NumContratos
  FROM (SELECT com2.IdComercial,
               com2.NomeCom,
               com2.IdEmpresa,
               COUNT(con2.IdContratos) AS quantidade
         FROM comerciais com2
              LEFT JOIN contratos con2 ON con2.IdComercial = com2.IdComercial
         GROUP BY com2.IdComercial) x
       INNER JOIN empresas emp ON x.IdEmpresa = emp.IdEmpresa
       JOIN (SELECT @curRank := 0, @curQtd := 0) r -- Aqui apenas zeramos os valores iniciais
 ORDER BY x.quantidade DESC
    
04.12.2016 / 19:35