web-dev-qa-db-fra.com

Comment convertir les lignes en colonnes et les interroger sur MySQL?

J'ai trois tables patients, qui contient le nom de mes patients, Contrôles qui représente les contrôles d'interface utilisateur pouvant être tirés pour chaque maladie et ControlsValues = Table contenant les valeurs des commandes soumises pour chaque patient

Permet de disposer de certaines données patients Table

|ID  | Name |
|-----------|
| 1  | Ara  |
| 2  | Sada |

Contrôles Tableau

|ID  | Text | Type     |
|-----------|----------|
| 1  | age  | textbox  |
| 2  |alergy| checkbox |

Alors le contrôlevalues table qui est là où je veux interroger à

|ID  | contrlId | value    | patientId |
|---------------|----------|-----------|
| 1  | 1        | 23       | 1         |
| 2  | 2        | true     | 1         |
| 3  | 1        | 26       | 2         |
| 4  | 2        | false    | 2         |

ici, mon problème se produit lorsque je veux retourner ce patient de ControlsValues table qui a le (controlId=1 ET value=23) et (controlId=2 ET value=true) Dans ce cas, la condition est sur deux rangées et non deux colonnes que ce n'est pas possible, je déséquilibre de modifier les lignes dans les colonnes en fonction de controlId mais je ne sais pas comment et je suis à la recherche de la recherche. 2 jours et vu beaucoup d'échantillons mais aucun d'entre eux m'a aidé à résoudre mon problème

2
rabar kareem

Vous pouvez essayer ce genre de "solution":

http://sqlfiddle.com/#!9/d33a95/18

Ce n'est pas une solution "pivot-table", mais peut-être que cela résout votre problème - si je l'ai bien compris.

Voici les tables/données utilisées:

CREATE TABLE `patients` (
`ID` INT NOT NULL AUTO_INCREMENT ,
`Name` VARCHAR( 50 ) NOT NULL ,
PRIMARY KEY ( `ID` )
);
CREATE TABLE `controls` (
`ID` INT NOT NULL AUTO_INCREMENT ,
`Text` VARCHAR( 50 ) NOT NULL ,
`Type` VARCHAR( 50 ) NOT NULL ,
PRIMARY KEY ( `ID` )
); 
CREATE TABLE `controlvalues` (
`ID` INT NOT NULL AUTO_INCREMENT ,
`ControlID` INT NOT NULL ,
`Value` VARCHAR( 50 ) NOT NULL ,
`PatientID` INT NOT NULL ,
PRIMARY KEY ( `ID` )
);
INSERT INTO `patients` (`Name`) VALUES ('Ara'), ('Sada'), ('Pada'), ('Lada');
INSERT INTO `controls` (`Text`, `Type`) VALUES ('age', 'textbox'), ('alergy', 'checkbox');
INSERT INTO `controlvalues` (`ControlID`, `Value`, `PatientID`) VALUES 
  (1, '23', 1), 
  (2, 'true', 1),
  (1, '25', 2),
  (2, 'false', 2),
  (1, '23', 3),
  (2, 'false', 3),
  (1, '23', 4), 
  (2, 'true', 4);

Et la requête:

SELECT PatientID, COUNT(PatientID) AS PatCount, p.Name
FROM controlvalues cv
LEFT JOIN patients p ON p.ID = cv.PatientID
WHERE ((ControlID = 1 AND Value = '23')
   OR (ControlID = 2 AND Value = 'true'))
GROUP BY PatientID
HAVING PatCount = 2
;
1
Billy G

Existe

Vous pouvez obtenir votre réponse sans avoir besoin de pivoter vos données. L'approche la plus directe (de mon point de vue) utilise EXISTS, qui reflète très directement votre déclaration:

SELECT
     p.ID, p.Name
FROM
     patients p
WHERE
    EXISTS 
    (
        SELECT 1
        FROM controlvalues cv1
        WHERE p.ID = cv1.PatientID
              AND cv1.controlId = 1
              AND cv1.value = '23'
    )
    AND
    EXISTS
    (
        SELECT 1
        FROM controlvalues cv2
        WHERE p.ID = cv2.PatientID
              AND cv2.controlId = 2
              AND cv2.value = 'true'
    ) ;

Qui va juste revenir:

[.____] id | Nom [.____] -: | : --- 
 1 | Ara 

Rejoindre

Comme alternative, vous pouvez modifier le EXISTS à JOINs:

SELECT
     p.ID, p.Name
FROM
     patients p
     JOIN controlvalues cv1 
         ON cv1.PatientID = p.ID AND cv1.controlId = 1 AND cv1.value = '23'
     JOIN controlvalues cv2 
         ON cv2.PatientID = p.ID AND cv2.controlId = 2 AND cv2.value = 'true' ;

qui retournera les mêmes résultats. La requête est plus concise, mais, à mon avis, un peu plus difficile à lire.

Les plans d'exécution (à partir de Mariambb 10.2) sont les mêmes, à condition que les bons indices (ou, dans mon cas, la clé primaire) ont été définis.


Pivot

Si vous voulez vraiment pivoter vos données, c'est comme ça que vous le feriez:

SELECT
    p.ID, p.Name,
    max(case when controlId = 1 then value end) AS controlId1 /* age */,
    max(case when controlId = 2 then value end) AS controlId2 /* allergy */
FROM
    patients p
    JOIN controlvalues cv ON cv.PatientId = p.ID 
GROUP BY
    p.ID ;

C'est la table pivotée:

[.____] id | Nom | contrôlé1 | contrôlé2 
 -: | :  : ---------- | : ---------- [.____] 1 | Ara | 23 | vrai 
 2 | Sada | 25 | faux

... Et c'est comme ça que vous l'utiliseriez:

SELECT
    pp.ID, pp.Name
FROM
    (
    SELECT
        p.ID, p.Name,
        max(case when controlId = 1 then value end) AS controlId1,
        max(case when controlId = 2 then value end) AS controlId2
    FROM
        patients p
        JOIN controlvalues cv ON cv.PatientId = p.ID 
    GROUP BY
        p.ID 
    ) AS pp
WHERE
    pp.controlId1 = '23' AND pp.controlId2 = 'true';
[.____] id | Nom [.____] -: | : --- 
 1 | Ara 

Le plan d'exécution pour le pivoté Tableau, cependant, pourrait probablement être pire (spécialement dans une grande table).

Vous pouvez tout vérifier en dbfiddle --- (ICI

1
joanolo