MYSQL issue selecting and sorting from separate tables - mysql

I am having difficulty assembling the proper sql statements to list and sort data based upon my needs. Below is the structure of two tables I need to select data from.
For each user in the users table, I need to list id, name, and key[a] and key[b] from users_nfo table.
Table users:
+----+------+
| id | name |
+----+------+
| 1 | aa |
| 2 | bb |
| 3 | cc |
| 4 | dd |
| 5 | ee |
+----+------+
Table users_nfo:
+----+-----+-----+-------+
| id | uid | key | value |
+----+-----+-----+-------+
| 1 | 1 | a | 22 |
| 2 | 1 | b | 47 |
| 3 | 2 | a | 38 |
| 4 | 2 | b | 16 |
| 5 | 3 | a | 27 |
| 6 | 3 | b | 67 |
| 7 | 4 | a | 75 |
| 8 | 4 | b | 67 |
| 9 | 5 | a | 63 |
| 10 | 5 | b | 67 |
+----+-----+-----+-------+
The result should be similar to this
Array result:
+----+------+---+---+
| id | name | a | b |
+----+------+---+---+
| 1 | aa |22 |47 |
| 2 | bb |38 |16 |
| 3 | cc |27 |67 |
| 4 | dd |75 |67 |
| 5 | ee |63 |67 |
+----+------+---+---+
Additionally, I need to be able to sort by any column key, such as sorted asc by b.
Help is appreciated. Thanks in advance!

The trick is to join the users_nfo (sic) table twice, and include the key column in the join condition. Like this:
SELECT u.ID, u.name, n1.value, n2.value from USERS u
JOIN users_nfo n1
ON u.id = n1.id AND n1.key = 'a'
JOIN users_nfo n2
ON u.id = n2.id AND n1.key = 'b'
ORDER BY n2.value ASC

Related

MySQL SELECT 8 unique users, then SELECT all records within those 8 users, then SELECT 4 most recent and available records of each 8 users?

I have a database (dates are just examples for order sake)...
---------------------
| user | item | date |
---------------------
| 1 | a | 123 |
| 3 | b | 124 |
| 1 | c | 125 |
| 2 | d | 126 |
| 5 | i | 127 |
| 4 | e | 128 |
| 6 | f | 129 |
| 9 | g | 130 |
| 3 | h | 131 |
| 9 | s | 132 |
| 1 | j | 133 |
| 2 | k | 134 |
| 1 | l | 135 |
| 1 | m | 136 |
| 1 | n | 137 |
| 8 | o | 138 |
| 5 | p | 139 |
| 9 | q | 140 |
| 7 | r | 141 |
---------------------
I would like to get all records up to the first 8 unique users, which would make the results...
---------------------
| user | item | date |
---------------------
| 1 | a | 123 |
| 3 | b | 124 |
| 1 | c | 125 |
| 2 | d | 126 |
| 5 | i | 127 |
| 4 | e | 128 |
| 6 | f | 129 |
| 9 | g | 130 |
| 3 | h | 131 |
| 9 | s | 132 |
| 1 | j | 133 |
| 2 | k | 134 |
| 1 | l | 135 |
| 1 | m | 136 |
| 1 | n | 137 |
| 8 | o | 138 |
---------------------
Then from those records, I'd like to get the most recent 4 records per unique user, making the results look like...
---------------------
| user | item | date |
---------------------
| 3 | b | 124 |
| 2 | d | 126 |
| 5 | i | 127 |
| 4 | e | 128 |
| 6 | f | 129 |
| 9 | g | 130 |
| 3 | h | 131 |
| 9 | s | 132 |
| 1 | j | 133 |
| 2 | k | 134 |
| 1 | l | 135 |
| 1 | m | 136 |
| 1 | n | 137 |
| 8 | o | 138 |
---------------------
Ideally I would be able to do this with one query. The closest I've been able to come is with this query:
SELECT users,
GROUP_CONCAT(items)
FROM db
GROUP BY users
ORDER BY date
LIMIT 8
But GROUP_CONCAT gives back all results for that user, not just the amount in the selection.
I've also tried...
SELECT users
FROM db AS u1
JOIN (SELECT DISTINCT users FROM db) AS u2 ON u1.users = u2.users
from another suggestion I found but this also didn't work.
I've tried a ton of other things that I didn't really save because they didn't work and I was pretty confident I'd figure it out, but it's been two weeks and I haven't got close. If any SQL gurus are out there that can point me in the right direction, that would be really great. Thanks.
Hoping this can help you.
--get all records up to the first 8 unique users,depending on there first order date
select distinct a.user,a.date as first_order_date from yourtable as a where
a.date = (select MIN(date) from yourtable as b where a.user=b.user) order by a.date LIMIT 8
Then using above result to get the most recent 4 records per unique user like below:
select * from yourtable as t where t.date in
(select date from yourtable as t2 where t.user=t2.user order by t2.date desc LIMIT 4 ) and
t.user in (select distinct a.user,a.date as first_order_date from yourtable as a
where a.date = (select MIN(date) from yourtable as b where a.user=b.user) order by a.date LIMIT 8)
References:
correlated subqueries
Example

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, ordering GROUP BY

I have a table that has some values in it, along with the time that value was taken against an associated ID from another table.
I am looking to retrieve the latest value for every item in that table, and then order by those latest values.
Here is an SQL fiddle, http://www.sqlfiddle.com/#!2/0be99
And here is text output.
'hist' table
| HIST_ID | HIST_ITEM_ID | HIST_VALUE | HIST_TIME |
|---------|--------------|------------|------------|
| 1 | 1 | 1 | 1420291000 |
| 2 | 1 | 2 | 1420292000 |
| 3 | 1 | 3 | 1420293000 |
| 4 | 1 | 5 | 1420294000 |
| 5 | 1 | 10 | 1420295000 |
| 6 | 1 | 50 | 1420296000 |
| 7 | 1 | 60 | 1420297000 |
| 8 | 1 | 77 | 1420298000 |
| 9 | 1 | 90 | 1420299000 |
| 10 | 1 | 101 | 1420300000 |
| 11 | 2 | 1 | 1420291000 |
| 12 | 2 | 3 | 1420292000 |
| 13 | 2 | 7 | 1420293000 |
| 14 | 2 | 9 | 1420294000 |
| 15 | 2 | 15 | 1420295000 |
| 16 | 2 | 21 | 1420296000 |
| 17 | 2 | 33 | 1420297000 |
| 18 | 2 | 35 | 1420298000 |
| 19 | 2 | 55 | 1420299000 |
| 20 | 2 | 91 | 1420300000 |
'items' table
| ITEM_ID | ITEM_TITLE |
|---------|------------|
| 1 | ABCD |
| 2 | XYZ123 |
So, I can do something like...
select * from hist
inner join items on hist_item_id = item_id
group by hist_item_id
order by hist_value desc
However this returns me a grouping that I cannot order. How can I order this grouping? I had a look at other similar questions on here but was unable to apply their solutions successfully to my query to produce the desire result.
The desired result here would be to return.
HIST_ITEM_ID | ITEM_TITLE | HIST_VALUE |
|------------|------------|------------|
| 1 | ABCD | 101 |
| 2 | XYZ123 | 91 |
You can use a join to get the most recent history item. Then you can join back to the history table and the item table to get additional information:
select h.*, i.item_title
from (select hist_item_id, max(hist_id) as max_hist_id
from hist
group by hist_item_id
) hh join
hist h
on h.hist_id = hh.max_hist_id join
items i
on i.item_id = hh.hist_item_id;
Here is a SQL Fiddle.
You should use MAX function and group by the item id. That would look like this:
SELECT i.item_id, i.item_title, MAX(h.hist_value)
FROM items AS i
INNER JOIN hist AS h
ON i.item_id = h.hist_item_id
GROUP BY i.item_id

MySQL how to find averages / day for different clients with different creation days

I've tried the following queries but unfortunately they don't work :(.
Worth mentioning that each customer has more than one CustomerUsers
select (a.TotalJobs / b.DaysActive) from
(select count(jr.id) as TotalJobs
from jobrequests jr, customers c, customerusers cu
where jr.customeruserid=cu.id
and cu.customerid=c.id
group by c.name) as a,
(select datediff(curdate(), from_unixtime(c.CreationTime)) as DaysActive
from customers c
group by c.name) as b
Please see below the tables
Jobs:
+----+--------------+
| ID | JobRequestID |
+----+--------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 1 |
| 5 | 1 |
| 6 | 2 |
| 7 | 2 |
| 8 | 3 |
| 9 | 3 |
| 10 | 3 |
| 11 | 4 |
| 12 | 4 |
| 13 | 5 |
| 14 | 5 |
| 15 | 6 |
| 16 | 7 |
| 17 | 8 |
| 18 | 8 |
| 19 | 9 |
| 20 | 10 |
+----+--------------+
JobRequests:
+----+---------------+
| ID | CustomeUserID |
+----+---------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 2 |
| 5 | 2 |
| 6 | 3 |
| 7 | 4 |
| 8 | 4 |
| 9 | 4 |
| 10 | 5 |
| 11 | 5 |
| 12 | 5 |
| 13 | 6 |
| 14 | 6 |
| 15 | 7 |
+----+---------------+
CustomerUsers:
+----+------------+
| ID | CustomerID |
+----+------------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 2 |
| 5 | 2 |
| 6 | 2 |
| 7 | 2 |
| 8 | 3 |
| 9 | 3 |
| 10 | 4 |
+----+------------+
Customers:
+----+------+--------------+
| ID | Name | CreationTime |
+----+------+--------------+
| 1 | a | 1415814194 |
| 2 | b | 1415814194 |
| 3 | c | 1415986994 |
| 4 | d | 1415986994 |
+----+------+--------------+
For the moment it returns 16 results (4X4), dividing each result from 1st sub-query to each result from the 2nd one (each of these sub-queries return 4 results). Can anyone please help me to get this to divide only 1 result from sub-query 1 to it's corespondent from sub-query 2?
Thank you in advance.
I suspect that you can do what you want this a query like this:
select c.name, count(*) / (datediff(curdate(), from_unixtime(c.CreationTime))
from customerusers cu join
jobrequests jr
on jr.customeruserid = cu.id join
customers c
on cu.customerid = c.id
group by c.name;
I don't see why you need two subqueries for this.
I'm guessing you need to join your results together -- as currently written, you're producing a cartesian product.
Try something like this adding c.id to each subquery (it's better to group by it presumably rather than the name):
select (a.TotalJobs / b.DaysActive)
from (
select c.id,
count(jr.id) as TotalJobs
from jobrequests jr
join customers c on jr.customeruserid=cu.id
join customerusers cu on cu.customerid=c.id
group by c.id) a join (
select c.id,
datediff(curdate(), from_unixtime(c.CreationTime)) as DaysActive
from customers c
group by c.id) b on a.id = b.id
Please note, I've updated your syntax to use the more standard join syntax.

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.