See What is the Employee Salary on a Certain Date

4

Does anyone know what a query would look like for a given employee's salary on a given date?

The salary readjustment history table for each employee has the following fields:

MATRICULADATASALARIO_ANTERIORREAJUSTENOMEADMISSAO_DATASALARIO_FIXO00028301.08.20094729.59270.53FULANODASILVA13.07.20095982.4800028301.07.20105000.12274.51FULANODASILVA13.07.20095982.4800028301.08.20115274.63369.22FULANODASILVA13.07.20095982.4800028301.08.20125643.85338.63FULANODASILVA13.07.20095982.48

ThefieldsADMISSAO_DATAandSALARIO_FIXOarefromtheEmployeestablemadeaJoinbetweenthetablesofHistoriasofAdjustmentswithOfficialswhereitgeneratedtheaboveresult.TheSALARY_FIXOfieldisfromtheEmployeestablewhereyoualwaysstorethecurrentsalaryafterthelastreset.Intheaboveresultitisat5982.48aftertheadjustmentmadeon01.08.2012whichwaspreviously5643.85.

HowwouldIdoit,forexample,whatwasthesalaryofthisregistrationclerk000283on01.05.2012?Iwasunabletodevelopaquerytocheckandcomparetoseehowmuchthesalarywasonthisdate.

Cananyonehelpme?

Sorak,howareyou?

IpassedthisquerytoMysqlBank,butthelastqueryrecordcannotcalculatethecurrentsalaryvalueafterthelastreset.Itlooksexactlylikethisintheimagebelow,theinitialvaluebeforethereset,andthedateuntilthefirstdateissetbeforethereset,butwhenthequeryrunsinSQLFiddleitrunscorrectly.

Thecorrectresultshouldlooklikethis:

    
asked by anonymous 14.09.2017 / 20:50

1 answer

3

Your entire problem occurs because the tables appear to be shaped too complicated for the problem to be solved. I suggest that, if possible, a salario table be created that has the value, beginning of validity and salary value, so the problem will be easily solved. If you can not change the structure, use the following query to extract the expected values:

SELECT x.matricula,
       x.tipo,
       x.salario,
       x.de,
       x.ate,
       x.reajuste
  FROM (-- Pega apenas os primeiros salários
        SELECT y1.*
          FROM (SELECT 'Contratação' AS tipo,
                       -- Caso ainda não possua reajuste utiliza a data atual como referência de final
                       COALESCE(DATE_SUB(fr.data, INTERVAL 1 DAY), CURDATE()) AS ate,
                       f.admissao_data AS de,
                       -- Caso não possua registro de reajuste utiliza o salario_fixo
                       COALESCE(fr.salario_anterior, f.salario_fixo) AS salario,
                       NULL AS salario_anterior,
                       NULL AS reajuste,
                       f.matricula
                  FROM funcionarios f
                       -- LEFT para caso seja o primeiro salário do funcionário
                       LEFT JOIN funcionarios_reajustes fr ON fr.matricula = f.matricula
                 -- Pega apenas os registros que não possuam anterior (primeiros)
                 WHERE NOT EXISTS(SELECT 1
                                    FROM funcionarios_reajustes fr2
                                   WHERE fr2.matricula = f.matricula
                                     AND fr2.data < fr.data)
                 ORDER BY fr.data) y1
         UNION
        -- Demais salários
        SELECT y2.*
          FROM (SELECT 'Reajuste' AS tipo,
                       CASE @matricula
                         -- Se não for o último utiliza a data do próximo reajuste menos 1 dia
                         WHEN fr.matricula THEN DATE_SUB(@data_proximo, INTERVAL 1 DAY)
                         -- Se for o último utiliza a data atual para referência de valor final
                         ELSE CURDATE()
                       END AS ate,
                       @data_proximo := fr.data AS de,
                       CASE @matricula
                         -- Se não for o último utiliza o valor do próximo reajuste
                         WHEN fr.matricula THEN @salario_anterior
                         -- Se for o último faz o cálculo
                         ELSE fr.salario_anterior + fr.reajuste
                       END AS salario,
                       @salario_anterior := fr.salario_anterior AS salario_anterior,
                       fr.reajuste,
                       @matricula := fr.matricula AS matricula
                  FROM funcionarios_reajustes fr
                 ORDER BY fr.matricula, fr.data DESC) y2
         ) x
 WHERE x.matricula = '000283'
   AND STR_TO_DATE('15/07/2011', '%d/%m/%Y') BETWEEN x.de AND x.ate
 ORDER BY x.matricula,
          x.de

The previous query:

  • Get the oldest registry value to find out the employee's first salary;
  • Performs the merge of the registry with the calculated ones;
  • Collect the data sorted by the newest dates first;
  • Uses control variables that are populated in the previous record (that is, newer). Thus the current salary is filled and the effective date;
  • In the result set the filter is performed by the desired date.

Note: I suggest that you create a VIEW with query above for ease of readability and reuse.

See working in SQL Fiddle .

If you want to follow the guideline of creating another table, the suggestion would be as follows:

CREATE TABLE IF NOT EXISTS salario(
  codigo    INT AUTO_INCREMENT,
  matricula VARCHAR(6),
  salario   DECIMAL(15, 2),
  inicio    DATE,
  CONSTRAINT pk_salario PRIMARY KEY(codigo),
  CONSTRAINT fk_salario_funcionarios FOREIGN KEY(matricula) REFERENCES funcionarios(matricula)
);

And for popular:

INSERT INTO salario(matricula,
                    salario,
                    inicio)
SELECT x.matricula,
       x.salario,
       x.de
  FROM (-- Pega apenas os primeiros salários
        SELECT y1.*
          FROM (SELECT 'Contratação' AS tipo,
                       -- Caso ainda não possua reajuste utiliza a data atual como referência de final
                       COALESCE(DATE_SUB(fr.data, INTERVAL 1 DAY), CURDATE()) AS ate,
                       f.admissao_data AS de,
                       -- Caso não possua registro de reajuste utiliza o salario_fixo
                       COALESCE(fr.salario_anterior, f.salario_fixo) AS salario,
                       NULL AS salario_anterior,
                       NULL AS reajuste,
                       f.matricula
                  FROM funcionarios f
                       -- LEFT para caso seja o primeiro salário do funcionário
                       LEFT JOIN funcionarios_reajustes fr ON fr.matricula = f.matricula
                 -- Pega apenas os registros que não possuam anterior (primeiros)
                 WHERE NOT EXISTS(SELECT 1
                                    FROM funcionarios_reajustes fr2
                                   WHERE fr2.matricula = f.matricula
                                     AND fr2.data < fr.data)
                 ORDER BY fr.data) y1
         UNION
        -- Demais salários
        SELECT y2.*
          FROM (SELECT 'Reajuste' AS tipo,
                       CASE @matricula
                         -- Se não for o último utiliza a data do próximo reajuste menos 1 dia
                         WHEN fr.matricula THEN DATE_SUB(@data_proximo, INTERVAL 1 DAY)
                         -- Se for o último utiliza a data atual para referência de valor final
                         ELSE CURDATE()
                       END AS ate,
                       @data_proximo := fr.data AS de,
                       CASE @matricula
                         -- Se não for o último utiliza o valor do próximo reajuste
                         WHEN fr.matricula THEN @salario_anterior
                         -- Se for o último faz o cálculo
                         ELSE fr.salario_anterior + fr.reajuste
                       END AS salario,
                       @salario_anterior := fr.salario_anterior AS salario_anterior,
                       fr.reajuste,
                       @matricula := fr.matricula AS matricula
                  FROM funcionarios_reajustes fr
                 ORDER BY fr.matricula, fr.data DESC) y2
         ) x
 WHERE x.matricula = '000283'
   AND STR_TO_DATE('15/07/2011', '%d/%m/%Y') BETWEEN x.de AND x.ate
 ORDER BY x.matricula,
          x.de
    
14.09.2017 / 21:20