How to replace reference ID with value - mysql

Let's say I have two tables:
+------------------------------------+
| `houses` |
+----+-------+-------+-------+-------+
| id | house | room1 | room2 | room3 |
+----+-------+-------+-------+-------+
| 1 | a | 1 | 1 | 2 |
| 2 | b | 1 | 3 | 1 |
| 3 | c | 2 | 2 | 1 |
+----+-------+-------+-------+-------+
+------------------------+
| `status` |
+-------------+----------+
| status_code | status |
+-------------+----------+
| 1 | empty |
| 2 | occupied |
| 3 | full |
+-------------+----------+
Now I want to change room1, room2 and room3's ids to the status from TABLE status like this:
+---------------------------------------------+
| `houses` |
+----+-------+----------+----------+----------+
| id | house | room1 | room2 | room3 |
+----+-------+----------+----------+----------+
| 1 | a | empty | empty | occupied |
| 2 | b | empty | full | empty |
| 3 | c | occupied | occupied | empty |
+----+-------+----------+----------+----------+
I know one solution, but I believe there is an easier way to do the same:
SELECT
h.*,
s1.status AS room1,
s2.status AS room2,
s3.status AS room3
FROM houses h
JOIN status s1 ON h.room1 = s1.status_code
JOIN status s2 ON h.room2 = s2.status_code
JOIN status s3 ON h.room3 = s3.status_code
UPDATE
MySQL pivot table solution is not suitable for me because the above example is a simplified version what I use. There are more than ten different status and writing a 40 line CASE function for a single room is not an opportunity. The status must come from the status table.

You can approach this as
SELECT id,house,
(
CASE
WHEN room1 > 0 THEN (SELECT status FROM status WHERE status_code=room1)
END) AS room1,
(
CASE
WHEN room2 > 0 THEN (SELECT status FROM status WHERE status_code=room2)
END) AS room2,
(
CASE
WHEN room3 > 0 THEN (SELECT status FROM status WHERE status_code=room3)
END) AS room3
FROM houses
Live DEMO

Related

How can I get a purchase check in MySQL

I am not able to figure out how I can get the following result with one MySQL Query:
I have two tables:
shop_items
| id | description | price | active |
+----+-------------+-------+--------+
| 1 | product_1 | 5 | 1 |
+----+-------------+-------+--------+
| 2 | product_2 | 10 | 1 |
+----+-------------+-------+--------+
| 3 | product_3 | 15 | 0 |
+----+-------------+-------+--------+
inventory_items (the shop_items a user purchased)
| id | item_id | user_id | active |
+----+---------+---------+--------+
| 1 | 2 | 1 | 1 |
+----+---------+---------+--------+
| 2 | 1 | 1 | 0 |
+----+---------+---------+--------+
I want to see all shop_items where active = 1 including a row called purchased = 0 or 1 based on inventory_items -> matching user_id (where user_id = something) and active = 1
Example output based on the data from above tables -> where user_id = 1:
| item_id | price | description | purchased |
+---------+-------+-------------+-----------+
| 1 | 5 | product_1 | 0 |
+---------+-------+-------------+-----------+
| 2 | 10 | product_2 | 1 |
+---------+-------+-------------+-----------+
What query do I need for this output?
Please note: I only need the result from ONE user_id which I can change within the query :)
Test
SELECT shop_items.*, COALESCE(inventory_items.active, 0) purchased
FROM shop_items
LEFT JOIN inventory_items ON shop_items.id = inventory_items.item_id
AND user_id = 1
WHERE shop_items.active = 1

Count with LEFT JOIN shows only one row

I have the following data in my database:
scu_banks:
---------------------------------
| id | type | name |
|-------------------------------|
| 1 | 1 | One |
| 2 | 1 | Two |
| 3 | 2 | Three |
| 4 | 3 | Four |
---------------------------------
scu_statement:
---------------------------------
| id | code | status |
|-----------------------------------|
| 1 | 1 | 0 |
| 2 | 1 | 1 |
| 3 | 2 | 0 |
| 4 | 1 | 0 |
-------------------------------------
What I want to do is I want to select all the rows in table scu_banks and calculate how many rows I have with the status 0. The data should be represented like:
--------------------------------------------------------------
| scu_banks.type | scu_banks.name | status | scu_banks.id |
--------------------------------------------------------------
| 1 | One | 2 | 1 |
| 1 | Two | 0 | 2 | //There is no row with status 0
| 2 | Three | 0 | 3 |
| 3 | Four | 0 | 4 |
--------------------------------------------------------------
When I run my sql statement I get the following data:
---------------------------------------------------------------
| scu_banks.type | scu_banks.name | status | scu_banks.id |
--------------------------------------------------------------
| 1 | One | 2 | 1 |
---------------------------------------------------------------
The data I get in this case is correct. 2 it the total count of all the rows in table scu_statement. The statement also dont shows the other rows in the database.
Does someone know what is wrong with my sql statement?
Here is my sql statement:
SELECT b.type 'scu_banks.type',
b.name 'scu_banks.name',
count(y.status) 'status',
b.id 'scu_banks.id'
FROM scu_banks b
LEFT JOIN (SELECT s.code, count(s.status) status
FROM scu_bankstatement s
WHERE status='0'
GROUP BY s.code) y
ON y.code = b.id
You need a GROUP BY in your outer query, otherwise the query simply counts status for all banks. You can also simplify your query by just LEFT JOINing the two tables on code/id and status = 0
SELECT b.type `scu_banks.type`,
b.name `scu_banks.name`,
COUNT(s.status) `status`,
b.id `scu_banks.id`
FROM scu_banks b
LEFT JOIN scu_statement s ON s.code = b.id AND s.status = 0
GROUP BY b.id, b.name, b.type
Output
scu_banks.type scu_banks.name status scu_banks.id
1 One 2 1
1 Two 1 2
2 Three 0 3
3 Four 0 4
Demo on dbfiddle

MySQL: Counting instances in many-to-many table

I'm creating a small employment site and am wondering if this is possible in MySQL: I have 3 sample jobs and I want to show all users who applied to job_id = 1 who have an application status of 'pending' while showing the total number of other 'pending' and 'pending' + 'hired' applications each user has.
I've been trying to get my head around this but I'm having problems. Is this something MySQL can do?
users
+----+-------+
| ID | name |
+----+-------+
| 1 | hanna |
| 2 | bob |
| 3 | rick |
+----+-------+
job
+--------+------------+
| job_id | jobname |
+--------+------------+
| 1 | 'waiter'|
| 2 | 'janitor'|
| 3 | 'cook'|
+--------+------------+
applications
+----------+---------+-----------+
| user_id | job_id | status |
+----------+---------+-----------+
| 1 | 1 | 'pending' |
| 1 | 2 | 'pending' |
| 1 | 3 | ' hired' |
| 2 | 1 | 'pending' |
| 3 | 1 | 'removed' |
+----------+---------+-----------+
My result set
+--------+---------+-----------+---------------+--------------------+
| job_id | user_id | status | count_pending | count_pendinghired |
+--------+---------+-----------+---------------+--------------------+
| 1 | 1 | 'pending' | 2 | 3 |
| 1 | 2 | 'pending' | 1 | 1 |
+--------+---------+-----------+---------------+--------------------+
The following query comes close to your suggested output. Note that it doesn't make sense to associate a single job_id with a given user, because a user may have multiple jobs. Likewise, it also doesn't make sense to associate a single status with a given user, since each record represents an aggregation of more than one status.
SELECT user_id,
SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) AS count_pending,
SUM(CASE WHEN status = 'pending' OR status = 'hired'
THEN 1 ELSE 0 END) AS count_pendinghired
FROM applications
GROUP BY user_id

MySQL Data Summary/Counts Table

I'm trying to put together a summary table that has counts of types of mail sent by group.
Hopefully the below is enough to explain what I mean.
Table 1 (senders)
| id | name | group_id |
+----+------+----------+
| 1 | mike | 1 |
| 2 | john | 1 |
| 3 | lucy | 2 |
| 4 | lobo | 3 |
Table 2 (mail)
| id | type | sender_id |
+----+----------+-----------+
| 1 | letter | 1 |
| 2 | postcard | 2 |
| 3 | postcard | 1 |
| 4 | letter | 2 |
| 5 | postcard | 2 |
| 6 | postcard | 4 |
Table 3 (groups)
| id | name | active |
+----+-------+--------+
| 1 | alpha | 1 |
| 2 | black | 1 |
| 3 | cero | 0 |
Ideal result
| group | letter | postcard | parcel |
+-------+--------+----------+--------+
| alpha | 2 | 3 | 0 |
| black | 0 | 0 | 0 |
So I need to get counts per mail type for active groups.
I've been working through examples (only learning MySQL) but when I think of this situation I'm just totally blank.
Have looked at the answers to Joining three tables to get summary data in MySQL but I don't quite understand how to translate the answers to my problem.
Any help is appreciated.
SELECT t.name,
MAX(CASE t.TYPE WHEN 'letter' THEN #CS:=#CS+1 ELSE 0 END ) letter,
MAX(CASE t.TYPE WHEN 'postcard' THEN #CS1:=#CS1+1 ELSE 0 END ) postcard ,
MAX(CASE t.TYPE WHEN 'parcel ' THEN #CS2:=#CS2+1 ELSE 0 END ) parcel
FROM
(SELECT
groups. name,
mail.type
FROM
groups
LEFT JOIN senders ON groups.id = senders.id
LEFT JOIN mail ON senders.id = mail.sender_id ) AS t
,(SELECT #CS:=0) CS ,(SELECT #CS1:=0) CS1 ,(SELECT #CS2:=0) CS2
You put this query
Select count(*) from senders s inner join mail m on s.id = s.sender_id inner join
groups g on s.groups_id = g.id group by m.type

Querying across 6 tables, is there a better way of doing this?

What I did was, I wanted each user to have their own "unique" numbering system. Instead of auto incrementing the item number by 1, I did it so that Bob's first item would start at #1 and Alice's number would also start at #1. The same goes for rooms and categories. I achieved this by creating "mapping" tables for items, rooms and categories.
The query below works, but I know it can definitely be refactored. I have primary keys in each table (on the "ids").
SELECT unique_item_id as item_id, item_name, category_name, item_value, room_name
FROM
users_items, users_map_item, users_room, users_map_room, users_category, users_map_category
WHERE
users_items.id = users_map_item.map_item_id AND
item_location = users_map_room.unique_room_id AND
users_map_room.map_room_id = users_room.room_id AND
users_map_room.map_user_id = 1 AND
item_category = users_map_category.unique_category_id AND
users_map_category.map_category_id = users_category.category_id AND
users_category.user_id = users_map_category.map_user_id AND
users_map_category.map_user_id = 1
ORDER BY item_name
users_items
| id | item_name | item_location |item_category |
--------------------------------------------------------
| 1 | item_a | 1 | 1 |
| 2 | item_b | 2 | 1 |
| 3 | item_c | 1 | 1 |
users_map_item
| map_item_id | map_user_id | unique_item_id |
----------------------------------------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
users_rooms
| id | room_name |
----------------------
| 1 | basement |
| 2 | kitchen |
| 3 | attic |
users_map_room
| map_room_id | map_user_id | unique_room_id |
----------------------------------------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
users_category
| id | room_name |
----------------------
| 1 | antiques |
| 2 | appliance |
| 3 | sporting goods |
users_map_category
| map_room_id | map_user_id | unique_category_id |
----------------------------------------------------
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 1 |
Rewriting your query with explicit JOIN conditions makes it more readable (while doing the same).
SELECT mi.unique_item_id AS item_id
, i.item_name
, c.category_name
, i.item_value
, r.room_name
FROM users_map_item mi
JOIN users_items i ON i.id = mi.map_item_id
JOIN users_map_room mr ON mr.unique_room_id = i.item_location
JOIN users_room r ON r.room_id = mr.map_room_id
JOIN users_map_category mc ON mc.unique_category_id = i.item_category
JOIN users_category c ON (c.user_id, c.category_id)
= (mc.map_user_id, mc.map_category_id)
WHERE mr.map_user_id = 1
AND mc.map_user_id = 1
ORDER BY i.item_name
The result is unchanged. Query plan should be the same. I see no way to improve the query further.
You should use LEFT [OUTER] JOIN instead of [INNER] JOIN if you want to keep rows in the result where no matching rows are found in the right hand table. You may want to move the additional WHERE clauses to the JOIN condition in this case, as it changes the outcome.