1
votes

Utilisation de variables définies par l'utilisateur MySQL avec la requête CakePHP3 select

Voici un exemple de ce dont j'ai besoin:
$query->select([
    $query->newExpr([
        '@salesTotal := SUM(Table.total)'
    ]),
]);

Dans mon cas, J'ai besoin d'un SUM (Table.total) pour être retourné dans une colonne salesTotal distincte, ainsi que réutilisé dans @ salesTotal / @ salesQuantity .

Voici ce que j'ai essayé:

1.

$query->select([
    $query->newExpr([
        '@salesTotal := ' . $query->func()->sum('Table.total')
    ]),
]);

Cela génère @salesTotal: = AND SUM (Table. total)

2.

$query->select([
    $query->newExpr([
        '@salesTotal := ',
        $query->func()->sum('Table.total')
    ]),
]);

Résultats dans Attention (4096): Objet de la classe Cake \ Database \ Expression \ FunctionExpression n'a pas pu être converti en chaîne

3.

SELECT 
    @earnings := (`house_rent`+`conveyance`+`medical`+`dearness`+`others_allowances`) AS earnings ,
    @deductions := (`income_tax`+`pro_tax`+`emp_state_insu`+`absence_fine`+`others_deductions`) AS deductions,
    @earnings - @deductions AS net_salary 
FROM 
    salary

Obtenir Avertissement (2): array_combine () : Les deux paramètres doivent avoir un nombre égal d'éléments CORE / src / ORM / ResultSet.php, ligne 527 . Ce n'est pas une bonne solution non plus.


4 commentaires

Je vous suggère d'essayer d'abord votre requête réelle en SQL brut, vous verrez probablement que cela ne fonctionnera pas.


@ndm Cela a fonctionné. Requête: SELECT (@salesTotal: = SUM (Table.total)), (@salesQuantity: = SUM (Table.quantity)), @ salesTotal / @ salesQuantity comme calculéPrix FROM ... ; résultats: 2187857.9060, 172523, 12.681543365


Sur quel mode SQL étrange exécutez-vous qui accorde l'accès aux résultats agrégés dans la liste de sélection ou calcule l'expression non agrégée plus tard? Êtes-vous sûr de ne pas accéder aux variables d'une requête exécutée précédemment?


@ndm Je suppose que vous avez raison: quand je mets cela dans une requête CakePHP, j'obtiens null sur une colonne calculée à partir des deux variables; les variables elles-mêmes semblent bien cependant. Cela signifie-t-il que la première partie de cette réponse (d'où j'ai eu l'idée) est incorrecte et / ou trompeur? Si oui, je voudrais commenter


3 Réponses :


0
votes

Inspiré par ce réponse , l'idée était de déclarer les variables, puis de les réutiliser pour calculer une autre valeur. Cette approche pourrait être utile en particulier avec des calculs complexes. J'ai utilisé \ Cake \ ORM \ Query :: newExpr () avec an conjonction modifiée .

De plus, selon la documentation MySQL sur les variables définies par l'utilisateur , j'ai essayé de les SET dès le départ.

De plus, le type de résultat par défaut d'une variable est basé sur son type au début de l'instruction. Cela peut avoir des effets inattendus si une variable contient une valeur d'un type au début d'une instruction dans laquelle une nouvelle valeur d'un type différent lui est également affectée.

Pour éviter des problèmes avec ce comportement, soit n'affectez pas de valeur à et ne lisez pas la valeur de la même variable dans une seule instruction, ou bien définissez la variable sur 0, 0.0 ou «» pour définir son type avant d'utiliser ça.

Voici la requête que j'ai obtenue:

$query->select([
    // regular calculated columns
    'salesTotal' => $query->func()->sum('ExampleTable.total'),
    'salesQuantity' => $query->func()->sum('ExampleTable.quantity'),
    // calculate again, without reusing any previously calculated results
    'calcPrice' => $query->newExpr([
        $query->func()->sum('ExampleTable.total'),
        $query->func()->sum('ExampleTable.quantity'),
    ])->setConjunction('/'),
]);

Remarque: les clés varSalesTotalCol et varSalesQuantityCol sont nécessaire, sinon j'obtenais

Attention (2): array_combine (): Les deux paramètres doivent avoir un nombre égal d'éléments [CORE / src / ORM / ResultSet.php, ligne 527]

Attention (4096): L'argument 1 passé à Cake \ ORM \ Entity :: __ construct () doit être du type array, booléen donné, appelé dans vendor / cakephp / cakephp / src / ORM / ResultSet.php à la ligne 601 et défini [CORE / src / ORM / Entity.php, ligne 48]

Dans le résultat final, calcPriceVar était nul, donc apparemment cela n'a pas fonctionné. Cela pourrait être dû à ceci:

L'ordre d'évaluation des expressions impliquant des variables utilisateur n'est pas défini. Par exemple, rien ne garantit que SELECT @a, @a: = @ a + 1 évalue d'abord @a, puis exécute l'affectation.

Je poste toujours ceci car je n'ai trouvé aucun exemple de création de requêtes similaires avec CakePHP ou d'utilisation de newExpr () de cette manière. Il m'a fallu un certain temps pour le comprendre, donc cela pourrait servir de référence.

L'option de travail que j'ai finalement choisie est la suivante:

$connection = $this->{'Example/Table'}->getConnection();
$connection->execute('SET @varSalesTotal = 0.0;');
$connection->execute('SET @varSalesQuantity = 0;');

$query = $this->{'Example/Table'}->find('all', [
    // ...
]);

$query->select([

    // calculate the @varSalesTotal variable
    // SQL: @varSalesTotal := SUM(ExampleTable.total)
    'varSalesTotalCol' => $query->newExpr([
        '@varSalesTotal',
        $query->func()->sum('ExampleTable.total'),
    ])->setConjunction(':='),

    // calculate the @varSalesQuantity variable
    // SQL: @varSalesQuantity := SUM(ExampleTable.quantity)
    'varSalesQuantityCol' => $query->newExpr([
        '@varSalesQuantity',
        $query->func()->sum('ExampleTable.quantity'),
    ])->setConjunction(':='),

    // attempt to calculate another value reusing the variables
    // SQL: @varSalesTotal/@varSalesQuantity AS calcPriceVar
    'calcPriceVar' => $query->newExpr([
        '@varSalesTotal',
        '@varSalesQuantity',
    ])->setConjunction('/'),

]);

Je n'aime pas le fait que je ne puisse pas réutiliser les calculs, et s'il y a une meilleure façon, je serais heureux d'apprendre.


0 commentaires

0
votes

En utilisant votre suggestion, j'ai fait ceci comme un CV pour utiliser la variable SET +:

Controller:

public function findWeeks(Query $query, array $options = []) {
     $conn = ConnectionManager::get('default');
     $conn->execute('SET @row_number = 0');
     $query = 'SELECT @row_number:=@row_number + 1) AS week_number FROM weeks';
     $stmt = $conn->execute($query);
     return $stmt;
}

Modèle:

$weeks = $this->Facturas->find('Weeks', $options)->fetchAll('assoc');


0 commentaires

0
votes

Puisque vous ne liez aucune valeur, vous pouvez simplement écrire les clauses littéralement sans aucune complexité. Comme ceci:

CREATE TABLE salary (
    id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    house_rent INT DEFAULT 0,
    conveyance INT DEFAULT 0,
    medical INT DEFAULT 0,
    dearness INT DEFAULT 0,
    others_allowances INT DEFAULT 0,
    income_tax INT DEFAULT 0,
    pro_tax INT DEFAULT 0,
    emp_state_insu INT DEFAULT 0,
    absence_fine INT DEFAULT 0,
    others_deductions INT DEFAULT 0
);

Je n'ai pas de table remplie pour tester cela et pour déterminer qu'il renvoie ce que vous voulez, mais il renvoie un tableau vide pour cette structure (plutôt que lancer des exceptions) en utilisant CakePHP:

$query
    ->select(array(
        '@earnings := (`house_rent`+`conveyance`+`medical`+`dearness`+`others_allowances`) AS earnings',
        '@deductions := (`income_tax`+`pro_tax`+`emp_state_insu`+`absence_fine`+`others_deductions`) AS deductions',
        '@earnings - @deductions AS net_salary',
    ))
    ->from('salary')
;


0 commentaires