Join multiple tables and search parameters - php

I have database with 2 tables, 1 for storing customers Id and for customer informations. The second table is ordered with key/value because I need to store undefined values without needing to alter customers table.
Table structure
table customers:
=================
id | status
=================
table customers_info:
=======================================
id | id_customer | key | value
=======================================
Content example:
table customers:
=================
id | status
1 | 1
2 | 1
3 | 1
==================
table customers_info
=======================================
id | id_customer | key | value
1 | 1 | name| Doe
2 | 1 | age | 25
3 | 1 | city| NY
4 | 2 | name| Smith
5 | 2 | age | 26
6 | 3 | age | 30
=======================================
I simply query my tables with
SELECT ci.id_customer AS CustomerId,
MAX(CASE WHEN ci.key = 'name' THEN ci.value ELSE '' END) AS CustomerName,
MAX(CASE WHEN ci.key = 'age' THEN ci.value ELSE '' END) AS CustomerAge,
MAX(CASE WHEN ci.key = 'city' THEN ci.value ELSE '' END) AS CustomerCity
FROM customers_info ci
GROUP BY ci.id_customer
ORDER BY ci.id_customer;
I can get all customers, but cannot query with age for parameter for example, I tried try search every customer with age 25 but with this query I can't get all values for the customer...
SELECT ci.id_customer AS CustomerId,
MAX(CASE WHEN ci.key = 'name' THEN ci.value ELSE '' END) AS CustomerName,
MAX(CASE WHEN ci.key = 'age' THEN ci.value ELSE '' END) AS CustomerAge,
MAX(CASE WHEN ci.key = 'city' THEN ci.value ELSE '' END) AS CustomerCity
FROM customers_info ci
LEFT JOIN customers_info on customers_info.age = '25'
GROUP BY ci.id_customer
ORDER BY ci.id_customer;
I would like to get
=======================================
id_customer | name | age | city
1 | Doe | 25 | NY
=======================================

I am not sure that the way you are doing this the best way (I am not sure I would use maximums to do this, nor do I think I would create the tables in the way you are doing it - since in this case you are performing several extra operations in order to have the key value table that you are using) - but here is my go -
First, my create statements:
create database CustomerLinkQuestion;
use CustomerLinkQuestion;
create table customers (
id int,
status int
);
create table customer_info (
id int,
id_customer int,
k varchar(255),
v varchar(255)
);
For whatever reason (and this may be because I don't use MySQL all that much) key and value didn't work (probably reserved) so I used k and v for your key and value segments.
The core concept is that you are going to have to use a sub-query of some sort or a view (a view would be better) -
select * From (
SELECT ci.id_customer AS CustomerId,
MAX(CASE WHEN ci.k = 'name' THEN ci.v ELSE '' END) AS CustomerName,
MAX(CASE WHEN ci.k = 'age' THEN ci.v ELSE '' END) AS CustomerAge,
MAX(CASE WHEN ci.k = 'city' THEN ci.v ELSE '' END) AS CustomerCity
FROM customer_info ci
GROUP BY ci.id_customer
ORDER BY ci.id_customer) as sub
where sub.CustomerAge = 25
the inside (the part in parenthesis and called sub) is exactly what you posted (except - for whatever reason on my test server I used "customer" and not "customers"). Then the sub-query, then the where clause cutting down the sub-query.
Again - I would strongly suggest you look at your schema and design since it seems like you are trying to create a code table for something that it simply doesn't make sense. You will end up with a fixed number of fields in the customer table, each tied to a specific customer. When you are doing what you are doing here, you are performing more database operations that are simply not necessary.
Best of luck!
Aleksei

Related

SQL query to make 2 sums from the same field

I am trying to have the total "amount" with "type" = 1 and with type = 2 by year of the "created_at" field.
Here is the table
+-----------------+------------------+------+
| Field | Type | Null |
+-----------------+------------------+------+-----
| id | int(10) unsigned | NO |
| type | int(10) unsigned | YES |
| type | varchar(255) | NO |
| amount | double(11,2) | NO |
| created_at | timestamp | YES |
| updated_at | timestamp | YES |
Here is my code (It only gives the total for type = 1):
DB::select(
'SELECT
year,
created_at,
type,
SUM(amount) AS total
FROM
(SELECT
YEAR(created_at) AS year,
amount,
created_at,
type
FROM table
WHERE type= 1) AS t
GROUP BY year, type
ORDER BY year;
');
thanks in advance for any help.
I suspect you really want conditional aggregation:
SELECT YEAR(created_at) AS year,
SUM(CASE WHEN type = 1 THEN amount ELSE 0 END) as amount_1,
SUM(CASE WHEN type = 2 THEN amount ELSE 0 END) as amount_2
FROM table
GROUP BY YEAR(created_at);
You just need one simple query for this. Take the year of the date, sum the amount and group by year and type. FYI, you have type as a field twice in your question. That's not possible, so I'm assuming that it's really some other field name the second time.
SELECT
YEAR(created_at) AS year,
type,
SUM(amount) AS amount
FROM mytable
GROUP BY YEAR(created_at), type
ORDER BY YEAR(created_at)
DEMO
Your question needs more clarification but I assumed you wanted to compare type 1 amount per year,created at, type to the total yearly amount.
select year(created_at), created_at, type,
sum(case when type =1 then amount else 0 end) as type1amount,
sum(amount) over (partition by year(created_at)) as yeartotal
from table
group by 1,2,3

Select 1 row from main table and multiple rows from related table [duplicate]

This question already has an answer here:
mySQL - Create a New Table Using Data and Columns from Three Tables
(1 answer)
Closed 6 years ago.
I have a mysql "users" table with:
id | name
1 | Jane Doe
and a second table "custom_user_fields" (related with userid):
id | userid | fieldname | value
1 | 1 | country | us
2 | 1 | address | test address
With PHP I have to create a list with all users and its related fields (if possibile with one single query).
Expected result:
id | name | country | address
1 | Jane Doe | us | test address
It's possible?
This is possible for a pre-defined set of related fields in table custom_user_fields. You can use conditional aggregation:
SELECT u.id, u.name,
MAX(CASE WHEN fieldname = 'country' THEN value END) AS country,
MAX(CASE WHEN fieldname = 'address' THEN value END) AS address
FROM users AS u
LEFT JOIN custom_user_fields AS c ON u.id = c.userid
GROUP BY u.id, u.name

Linking multiple rows from table B to one row in table A in MySQL

I have two tables, for simplicity, table A and table B (Note, I don't have the authority to change the structure of these tables, so I'm asking this question to get around a bad database design).
Table A has two columns:
"id" - is the unique identifier.
"customer_id" - is the customer's ID.
So table A holds a list of customer IDs.
Table B holds properties about customers. But it does it in a weird way (again, I didn't set this up, and I can't change it). Table B has [NUMBER] columns:
"id" - is the unique identifier.
"customer_id" - is the customer's ID.
"key" - is the name of the key/value pair
"value" - is the value of the key/value pair
So table B holds key/value pairs that are linked to customers by their ID.
I could join these two tables to get something like this:
+----+-------------+------------+-------+
| id | customer_id | key | value |
+----+-------------+------------+-------+
| 0 | 5 | first_name | Bob |
| 1 | 5 | last_name | Jones |
| 2 | 6 | first_name | Sally |
| 3 | 6 | last_name | Sue |
+----+-------------+------------+-------+
But as you can see, that can be difficult to manage because information about one customer is on two different rows. What would be ideal is something like this:
+----+-------------+------------+-----------+
| id | customer_id | first_name | last_name |
+----+-------------+------------+-----------+
| 0 | 5 | Bob | Jones |
| 1 | 6 | Sally | Sue |
+----+-------------+------------+-----------+
With all of the customer's data on one row.
Is there a way to do this in a SQL query so that I don't have to mess with the results in PHP? Or will I have to pick through the data in PHP?
Given that you cannot alter the table structure, one approach is to use subselects on the customer_id and key:
SELECT
tableA.id,
tableA.customer_id,
(
SELECT
tableB.`value`
FROM tableB
WHERE tableB.customer_id = tableA.customer_id
AND tableB.`key` = 'first_name'
) AS first_name,
(
SELECT
tableB.`value`
FROM tableB
WHERE tableB.customer_id = tableA.customer_id
AND tableB.`key` = 'last_name'
) AS last_name
FROM tableA
Note: Performance-wise this query might suck. But, if you are out of options, maybe the slow query will drive the people who make decisions to allow the structure to be changed.
Use Group_concat or Group by
Select *,Group_concat(value) as full_name
From b left join a on b.customer_id=a.customer_id
Group by customer_id
One method is conditional aggregation:
select (#rn := #rn + 1) as id, customer_id,
max(case when `key` = 'first_name' then value end) as first_name,
max(case when `key` = 'last_name' then value end) as last_name
from b cross join
(select #rn := 0) params
group by customer_id;
I'm not sure what table a would be used for, perhaps for filtering the customer ids.
This will do what you want:
SELECT A.customer_id, B.value, B_1.value
FROM (A INNER JOIN B ON A.customer_id = B.customer_id)
INNER JOIN B AS B_1 ON B.customer_id = B_1.customer_id
WHERE (((B.key)="first_name") AND ((B_1.key)="last_name"));

MySQL Group by column, then count by another column

I have a challenging MySQL problem that is beyond my basic knowledge, I would really appreciate any help.
I currently have the following query:
select users.userid, CAST(posts.time AS DATE)
FROM users INNER JOIN posts ON users.post_id = posts.id
Sample output:
userid | CAST(posts.time AS DATE)
1............2015-01-05
2............2015-02-06
2............2015-04-07
2............2015-04-07
3............2015-04-07
1............2015-02-06
7............2015-01-05
userid can repeat itself, there could be 10 different rows with userid = 1; same goes for the date column. I would like to count how many rows each userid had for each distinct date. Based on the above data, the output should be:
-----------------------1----------2--------3---------4--------5--------6-------7
2015-01-05.............1..........0........0.........0........0........0.......1
2015-02-06.............1..........1........0.........0........0........0.......0
2015-04-07.............0..........2........1.........0........0........0.......0
I have 7 users in total. I would like to further replace the user id with a name that I define; e.g. I would define 1 in the heading/title to be displayed as Mike, 2 to be displayed as George, and so forth...
Is it possible? Thanks everyone.
If you have 7 users only, and only ever will, pivoting the data is not too difficult:
select date(posts.time),
count(case when userid = 1 then userid end) as `1`,
count(case when userid = 2 then userid end) as `2`,
count(case when userid = 3 then userid end) as `3`,
count(case when userid = 4 then userid end) as `4`,
count(case when userid = 5 then userid end) as `5`,
count(case when userid = 6 then userid end) as `6`,
count(case when userid = 7 then userid end) as `7`
users INNER JOIN posts ON users.post_id = posts.id
group by date(posts.time)
demo here
If your number of users is variable, or prone to change - it becomes annoying and you'd be better off looking to your application language to take care of it.
Here's what I have (I didn't complete it for you):
SELECT date, SUM(id_1) AS Mike, SUM(id_2) AS George FROM (SELECT CASE id WHEN 1 THEN 1 ELSE 0 END as id_1, CASE id WHEN 2 THEN 1 ELSE 0 END as id_2, date FROM test_dates) as tmp GROUP BY date;
+------------+------+--------+
| date | Mike | George |
+------------+------+--------+
| 2015-01-05 | 1 | 0 |
| 2015-02-06 | 1 | 1 |
| 2015-04-07 | 0 | 2 |
+------------+------+--------+
The trick of substituting a summation of 1s when what you want is a count is a common reporting trick that is worth remembering. Blew my mind when I first saw it.

Joining most recent column entry to MySQL query result

I have a PHP/MySQL based solution for displaying an ID and the number of entries that ID has on a MySQL database table in the past day, and year to date.
The information is simply output to a table.
We would also be able to quickly see the most recent value for a particular column associated with each ID on this same table.
The tables in use have the following descriptions:
TABLE: tags
id int auto_increment
Tag int(8)
Timestamp timestamp
Battery varchar(3) // Status for rfid tags battery condition
TABLE: tag_vehicle
id int auto_increment
Tag int(8)
VehicleId varchar(10)
And here is my MySQL query
SELECT tags.Tag,
sum(CASE WHEN year(Timestamp) = year(CURDATE()) then 1 else 0 end) as ytd,
sum(CASE WHEN date(Timestamp) = date(CURDATE()) then 1 else 0 end) as today,
tag_vehicle.VehicleId,
MAX(Timestamp) as latest
FROM tags
INNER JOIN tag_vehicle
ON tags.Tag = tag_vehicle.Tag
GROUP BY VehicleNumber;
This generates a table with these columns
| Tag | ytd | today | VehicleId | Timestamp |
|1234 | 300 | 12 | BUS1234 | 2014-09-22 |
I'm simply looking to add to my table the latest value of Battery that matches each Tag. I've been trying all morning to produce this result but I haven't had any luck.
| Tag | ytd | today | VehicleId | Timestamp | Battery |
|1234 | 300 | 12 | BUS1234 | 2014-09-22 | ok |
I'm not an expert on MySQL and its starting to feel like the query is too messy. I'm having a hard time figuring out how to get this battery value.
How should I go about obtaining the latest entry for Battery and matching it to the right row?
Is there some way to do it all in 1 (perhaps cleaner) query, or should I make 2 queries and match Battery columns to Tag columns?
You should be able to do this with the substring_index()/group_concat() trick:
SELECT t.tag,
sum(CASE WHEN year(Timestamp) = year(CURDATE()) then 1 else 0 end) as ytd,
sum(CASE WHEN date(Timestamp) = date(CURDATE()) then 1 else 0 end) as today,
tv.VehicleId,
MAX(Timestamp) as latest,
substring_index(group_concat(t.battery order by t.timestamp desc), ',', 1)
FROM tags t INNER JOIN
tag_vehicle tv
ON t.Tag = tv.Tag
GROUP BY VehicleNumber;
The tag column doesn't look right because it comes from an indeterminate row.

Categories