MySQL : How to do a Matrix combining three tables - mysql

I have the three following SQL Tables :
+-------------------------+
| SHOP |
+---------+---------------+
| shop_id | shop_label |
+---------+---------------+
| 1 | Shop Paris |
| 2 | Shop Madrid |
| 3 | Shop New York |
| 4 | Shop Tokyo |
+---------+---------------+
+----------------------------+
| PRODUCT |
+------------+---------------+
| product_id | product_label |
+------------+---------------+
| 1 | Pen |
| 2 | Workbook |
| 3 | Smartphone |
| 4 | Computer |
| 5 | Chair |
+------------+---------------+
+-------------------------------------------------------+
| COMMAND LINE |
+---------+------------+----------+---------------------+
| fk_shop | fk_product | quantity | date |
+---------+------------+----------+---------------------+
| 1 | 1 | 10 | 2021-10-20 12:10:59 |
| 4 | 3 | 1 | 2021-10-23 12:11:07 |
| 2 | 2 | 3 | 2021-10-29 12:12:07 |
| 1 | 2 | 8 | 2021-10-30 12:12:37 |
| 1 | 1 | 5 | 2021-11-03 13:10:07 |
+---------+------------+----------+---------------------+
And now I'm trying to make a matrix of all the products as COLUMNS and all the shops as ROWS showing how much product were buyed. I would like to make a query to retrieve this result :
+------------+------------+-------------+---------------+------------+
| | Shop Paris | Shop Madrid | Shop New York | Shop Tokyo |
+------------+------------+-------------+---------------+------------+
| Pen | 15 | 8 | | |
| Workbook | | 3 | | |
| Smartphone | | | | 1 |
| Computer | | | | |
| Chair | | | | |
+------------+------------+-------------+---------------+------------+
Do you know a way to do something like this ? I saw a long time ago a query with the WITH operator to create matrix, but I don't remember it really well...
Thanks for your help.

in oracle or sqlserver there is PIVOT() to use , but in MYSQL its not.
So something like this can be done:
select p.product_label ,
sum(case when s.shop_id = 1 then cl.quantity else 0 end) Shop_Paris ,
sum(case when s.shop_id = 2 then cl.quantity else 0 end) Shop_madrid ,
sum(case when s.shop_id = 3 then cl.quantity else 0 end) Shop_newyork ,
sum(case when s.shop_id = 4 then cl.quantity else 0 end) Shop_tokyo
from command_line cl
inner join product p on p.product_id = cl.fk_product
inner join shop s on s.shop_id = cl.fk_shop
group by p.product_label

Related

MySql - How to sort vertically stored data to order by one of field's values horizontally?

I have 3 mysql tables named records, fields and fields_values.
Records table consists of meta data about records and record id.
Fields table
No. of fields for a record are variable, and can be added dynamically.
Fields values contain info about fields shown on the record form eg: what type of fields is it, whether it is required or no etc.
Fields values table
This table contains actual data of records for each field.
For example, these tables have data as below:
Records Table:
+----+-------------+------------+--------+
| id | category_id | created_by | status |
+----+-------------+------------+--------+
| 1 | 1 | 10 | 1 |
| 2 | 1 | 10 | 1 |
| 3 | 1 | 10 | 1 |
+----+-------------+------------+--------+
Fields Table:
+----+------------+------------+------+--------+----------+------------+
| id | title | alias | type | status | required | created_by |
+----+------------+------------+------+--------+----------+------------+
| 1 | First Name | first_name | text | 1 | 1 | 100 |
| 2 | Last Name | last_name | text | 1 | 1 | 100 |
| 3 | City | city | text | 1 | 1 | 100 |
| 4 | State | state | text | 1 | 1 | 100 |
| 5 | Country | country | text | 1 | 1 | 100 |
| 6 | Mobile | mobile | text | 1 | 1 | 100 |
+----+------------+------------+------+--------+----------+------------+
Fields Values Table:
+----+----------+-----------+------------+
| id | field_id | record_id | value |
+----+----------+-----------+------------+
| 1 | 1 | 1 | Andy |
| 2 | 2 | 1 | A |
| 3 | 3 | 1 | Manchester |
| 4 | 4 | 1 | NWE |
| 5 | 5 | 1 | UK |
| 6 | 6 | 1 | 1234567898 |
| 7 | 1 | 2 | Sandy |
| 8 | 2 | 2 | B |
| 9 | 3 | 2 | NYC |
| 10 | 4 | 2 | NY |
| 11 | 5 | 2 | USA |
| 12 | 6 | 2 | 1234567891 |
| 13 | 1 | 3 | Mandy |
| 14 | 2 | 3 | P |
| 15 | 3 | 3 | Mumbai |
| 16 | 4 | 3 | MH |
| 17 | 5 | 3 | IN |
| 18 | 6 | 3 | 1234567893 |
+----+----------+-----------+------------+
And, I want to records as below and want to sort it based on one of the fields as selected by user eg: country
+----+------------+-----------+------------+-------+-----------+------------+
| id | first_name | last_name | city | state | country ^ | mobile |
+----+------------+-----------+------------+-------+---------+------------+
| 3 | Mandy | P | Mumbai | MH | IN | 1234567893 |
| 1 | Andy | A | Manchester | NWE | UK | 1234567898 |
| 2 | Sandy | B | NYC | NY | USA | 1234567891 |
+----+------------+-----------+------------+-------+-----------+------------+
How do I sort such vertically stored data to order by one of field's values in a single query so that it can be shown horizontally?
Just to observe that it would be more usual to write that this way...
SELECT r.id
, MAX(CASE WHEN field_id = 1 THEN value END) first_name
, MAX(CASE WHEN field_id = 2 THEN value END) last_name
, MAX(CASE WHEN field_id = 3 THEN value END) city
, MAX(CASE WHEN field_id = 4 THEN value END) state
, MAX(CASE WHEN field_id = 5 THEN value END) country
, MAX(CASE WHEN field_id = 6 THEN value END) mobile
FROM records r
LEFT
JOIN fields_values v
ON v.record_id = r.id
GROUP
BY r.id;

Mysql 2 select staments with value from statement 1 used in statement 2

How to join these to in one sql statement?
I have these 2 simple tables.
3_Referee_Matches
+------------------------------------+
| ID | CountryCode | RefereeUrlCode |
| 1 | eng | mike-jean |
| 2 | eng | mike-jean |
| 3 | eng | mike-jean |
| 4 | eng | mike-jean |
| 5 | spa | hulo-pape |
| 6 | ita | enri-tolsi |
| 7 | ita | enra-ean |
| 8 | ita | enra-ean |
+------------------------------------+
3_Players
+----------------------------------------------------+
| ID | MatchID | Name | PlayerUrlCode | Yellow |
| 1 | 1 | Mike Bell | mike-bell | 1 |
| 2 | 2 | Mike Bell | mike-bell | 1 |
| 3 | 3 | Thoms Tim | thoms-tim | 1 |
| 4 | 4 | Jean Claod | jean-claod | 0 |
| 5 | 33 | Thoms Tim | thoms-tim | 1 |
| 6 | 44 | Fis Most | fis-most | 0 |
| 7 | 54 | Geni Toens | geni-toens | 1 |
| 8 | 67 | Geni Toens | geni-toens | 1 |
+----------------------------------------------------+
Today i use these 2 select. But need help to combine them into one.
select 1:
SELECT rm.*, p.PlayerUrlCode AS VALUEtoBEusedAGAIN, COUNT(p.ID) AS YellowCounter
FROM 3_Referee_Matches rm
JOIN 3_Players p ON rm.ID = p.MatchID
WHERE rm.CountryCode = 'eng' AND rm.RefereeUrlCode = 'mike-jean'
AND p.Yellow>0
GROUP BY p.Name
select 2:
SELECT COUNT(rm.ID) AS Counter
FROM 3_Referee_Matches rm
JOIN 3_Players p ON rm.ID = p.MatchID
WHERE rm.RefereeUrlCode='mike-jean'
AND p.PlayerUrlCode='VALUEtoBEusedAGAIN'
Result should be like this:
+--------------------------------------+
| Name | YellowCounter | Counter |
| Mike Bell | 2 | 2 |
| Jean Claod | 1 | 1 |
+--------------------------------------+
Showing that Mike Bell Got 2 yellow cards in 2 matches.
Your first query is nearly there. First, remove the extraneous columns from the select clause. count the number of "players" (really player information for each match) and sum their yellow cards.
select
p.name,
sum(yellow) as YellowCounter,
count(p.id) as Counter
from 3_Referee_Matches rm
join 3_Players p on rm.ID = p.MatchID
where rm.CountryCode = 'eng'
and rm.RefereeUrlCode = 'mike-jean'
group by p.name;
+------------+---------------+---------+
| name | YellowCounter | Counter |
+------------+---------------+---------+
| Mike Bell | 2 | 2 |
| Thoms Tim | 1 | 1 |
| Jean Claod | 0 | 1 |
+------------+---------------+---------+
I assume the example has Thoms and Jean reversed.

Get rows that doesn't match on another table

I have this working query that generate data the way i like it to happened but now i want it to add another clause statement that will get rows from tbl_loan_master that was not on the tbl_loanledger.
Example Data:
tbl_borrowers
------------------------------------------
| id | first_name | last_name | deleted |
| 1 | Joe | Smith | 0 |
| 2 | Lily | Mag | 0 |
| 3 | Zen | Green | 0 |
| 4 | Kim | Chan | 0 |
| 5 | Bob | Mac | 1 |
| 6 | Ben | Cork | 0 |
------------------------------------------
tbl_loan_master
----------------------------------------------------------------------
| id | borrowers_id | loan | date_created | due_date | deleted |
| 1 | 4 | 300 | 2016/04/28 | 2017/04/28 | 0 |
| 2 | 1 | 100 | 2016/05/05 | 2017/05/05 | 0 |
| 3 | 2 | 500 | 2016/06/08 | 2017/06/08 | 0 |
| 4 | 1 | 200 | 2016/06/13 | 2017/06/13 | 0 |
| 5 | 3 | 150 | 2016/06/15 | 2017/06/15 | 0 |
| 6 | 6 | 50 | 2016/06/16 | 2017/06/16 | 0 |
----------------------------------------------------------------------
tbl_loanledger
------------------------------------------------------------------------------
| id | borrowers_id | loanmaster_id | payment | balance| date_created | deleted
| 1 | 4 | 1 | 50 | 250 | 2016/05/28 | 0
| 2 | 1 | 2 | 20 | 80 | 2016/05/25 | 0
| 3 | 1 | 2 | 30 | 50 | 2016/06/01 | 0
| 4 | 2 | 3 | 100 | 400 | 2016/06/09 | 0
| 5 | 2 | 3 | 50 | 350 | 2016/06/10 | 0
| 6 | 3 | 4 | 50 | 150 | 2016/06/16 | 0
------------------------------------------------------------------------------
Here is the working query:
$query = "SELECT `tbl_borrowers`.* , `tbl_loanledger`.*, `tbl_loan_master`.*
FROM `tbl_borrowers`
LEFT JOIN `tbl_loanledger`
ON `tbl_borrowers`.id = `tbl_loanledger`.borrower_id
LEFT JOIN `tbl_loan_master`
ON `tbl_loan_master`.id = `tbl_loanledger`.loanmaster_id
WHERE `tbl_borrowers`.deleted = 0 AND `tbl_loanledger`.deleted = 0 AND MONTH ( `tbl_loanledger`.date_created) = MONTH(CURRENT_DATE)
GROUP BY `tbl_loanledger`.borrower_id
ORDER BY `tbl_borrowers`.last_name";
The expected result will output the borrowers with their last transaction on the current month (which is June) in loan ledger and also in loans accounts. Like Ben Cork which is not on the loan ledger, he is on the loan accounts but i want to output him on the result set. The deleted column means if it is 0 it indicates it is active if it is 0 it means it has been deleted.
Expected Result:
|First Name | Last Name | Due Date | Balance |
| Ben | Cork | 2017/06/16 | 50 |
| Joe | Smith | 2017/06/13 | 50 |
| Lily | Mag | 2017/06/08 | 350 |
| Zen | Green | 2017/06/15 | 150 |
Try following;)
select
tb.first_name, tb.last_name, coalesce(tlm.Loan, 0) as Loan, coalesce(t.`Amount Paid`, 0) as `Last Amount Paid`
from tbl_borrowers tb
left join tbl_loan_master tlm
on tb.id = tlm.borrowers_id
left join (
select t1.*
from tbl_loanledger t1
inner join (
select max(id) as id
from tbl_loanledger
group by borrowers_id, loanmaster_id
) t2 on t1.id = t2.id
) t
on tb.id = t.borrowers_id
and tlm.id = t.loanmaster_id
SQLFiddle DEMO HERE

How to get count of combinations from database?

How to get count of combinations from database?
I have to database tables and want to get the count of combinations. Does anybody know how to put this in a database query, therefore I haven't a db request for each trip?
Trips
| ID | Driver | Date |
|----|--------|------------|
| 1 | A | 2015-12-15 |
| 2 | A | 2015-12-16 |
| 3 | B | 2015-12-17 |
| 4 | A | 2015-12-18 |
| 5 | A | 2015-12-19 |
Passengers
| ID | PassengerID | TripID |
|----|-------------|--------|
| 1 | B | 1 |
| 2 | C | 1 |
| 3 | D | 1 |
| 4 | B | 2 |
| 5 | D | 2 |
| 6 | A | 3 |
| 7 | B | 4 |
| 8 | D | 4 |
| 9 | B | 5 |
| 10 | C | 5 |
Expected result
| Driver | B-C-D | B-D | A | B-C |
|--------|-------|-----|---|-----|
| A | 1 | 2 | - | 1 |
| B | - | - | 1 | - |
Alternative
| Driver | Passengers | Count |
|--------|------------|-------|
| A | B-C-D | 1 |
| A | B-D | 2 |
| A | B-C | 1 |
| B | A | 1 |
Has anybody an idea?
Thanks a lot!
Try this:
SELECT Driver, Passengers, COUNT(*) AS `Count`
FROM (
SELECT t.ID, t.Driver,
GROUP_CONCAT(p.PassengerID
ORDER BY p.PassengerID
SEPARATOR '-') AS Passengers
FROM Trips AS t
INNER JOIN Passengers AS p ON t.ID = p.TripID
GROUP BY t.ID, t.Driver) AS t
GROUP BY Driver, Passengers
The above query will produce the alternative result set. The other result set can only be achieved using dynamic sql.
Demo here

Mysql query to convert table from long format to wide format

I have a table called ContactAttrbiutes which contains a list of each contacts' attributes. The kind of data stored for these contacts include: Title, Forename, Surname telephone number etc.
Current Table
+-------------+-----------+------------------------------+
| attributeId | ContactId | AttributeValue |
+-------------+-----------+------------------------------+
| 1 | 5 | Lady |
| 2 | 5 | Elizabeth |
| 3 | 5 | E |
| 4 | 5 | Anson |
| 5 | 5 | |
| 6 | 5 | |
| 7 | 5 | |
| 8 | 5 | |
| 10 | 5 | 0207 72776 |
| 11 | 5 | |
| 12 | 5 | 0207 22996 |
| 13 | 5 | 0207 72761 |
| 14 | 5 | |
| 15 | 5 | |
| 60 | 5 | Lloyds |
| 61 | 5 | |
| 1 | 10 | Mr |
| 2 | 10 | John |
| 3 | 10 | J C |
| 4 | 10 | Beveridge |
| 5 | 10 | Esq QC |
| 6 | 10 | Retired |
| 7 | 10 | |
| 8 | 10 | |
| 10 | 10 | 0207 930 |
| 11 | 10 | |
| 12 | 10 | |
| 13 | 10 | 0207 930 |
| 14 | 10 | |
| 15 | 10 | |
| 60 | 10 | |
| 61 | 10 | |
+-------------+-----------+------------------------------+
However I would like to run a query to create a table that looks like...
New Table
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| ContactId | AttributeValue_Title | AttributeValue_ForeName |AttributeValue_Initial | AttributeValue_Surname |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| 5 | Lady | Elizabeth | E | Anson |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
| 10 | Mr | John | J C | Beveridge |
+-----------+----------------------+-------------------------+-----------------------+------------------------+
I am sure there is a very simple answer but I have spent hours looking. Can anyone help?
The above is only a small extract of my table, I have 750,000 contacts. In addition I would like the final table to have more columns than I have described above but they will come from different Attributes with the existing table.
Thank you very much in advance.
try this
SELECT ContactId ,
max(CASE when attributeId = 1 then AttributeValue end) as AttributeValue_Title ,
max(CASE when attributeId = 2 then AttributeValue end )as AttributeValue_ForeName ,
max(CASE when attributeId = 3 then AttributeValue end )as AttributeValue_Initial ,
max(CASE when attributeId = 4 then AttributeValue end) as AttributeValue_Surname
from Table1
group by ContactId
DEMO HERE
if you want to make your result more longer for other attributeId then just add a case statment as in the code.
SELECT
t_title.AttributeValue AS title,
t_name.AttributeValue AS name,
...
FROM the_table AS t_title
JOIN the_table AS t_firstname USING(contact_id)
JOIN ...
WHERE
t_title.attributeId = 1 AND
t_firstname.attributeId = 2 AND
...
EAV "model" is an antipattern in most cases. Are you really going to have a variable number of attributes? If yes, then no-SQL solution might be more appropriate than a relational database.