Order by single column using multiple where clauses - mysql

This a hard question to know how to ask properly, but here goes...
This is the basic format of my table (the actual table has many rows and multiple lang_ids):
----------------------------------
| id | lang_id | key | text |
----------------------------------
| 1 | 1 | k_foo | foo |
----------------------------------
| 2 | 1 | k_bar | bar |
----------------------------------
| 3 | 2 | k_bar | le bar |
----------------------------------
| 4 | 2 | k_foo | le foo |
----------------------------------
What I want to do is return the rows with WHERE lang_id = 2 but order them by results of WHERE lang_id = 1 like so:
----------------------------------
| id | lang_id | key | text |
----------------------------------
| 4 | 2 | k_foo | le foo |
----------------------------------
| 3 | 2 | k_bar | le bar |
----------------------------------
I am driving myself nuts trying to figure it out. I've searched for hours but keep getting results for ordering by multiple columns instead of multiple results of a single column.
I've tried joining it, unioning it, and subqueries but I either return hundreds of rows, or none!

SELECT
l2.id,
l2.lang_id,
l2.key,
l2.text
FROM
language l1
JOIN
language l2
ON l2.key = l1.key
WHERE
l1.lang_id = 1
AND
l2.lang_id = 2
ORDER BY
l1.id -- Replace with whatever column you actually want to order by.

Related

Deleting a Parent Record from a Look-up Table using a Join

I'm having trouble with SqlFiddle hanging so will try to explain simply with tables here.
I have three tables:
Orders
Items
Look-Up Table
The Look-Up table is necessary because of the way I receive data so I extract it into a table with single-items with OrderID and ItemID
Item Types
+----+----------+---------+
| ID | ParentID | Name |
+----+----------+---------+
| 1 | NULL | Clothes |
| 2 | 1 | Shirts |
| 3 | NULL | Food |
| 4 | 3 | Steak |
+----+----------+---------+
Orders
+----+---------+--------+
| ID | OrderID | ItemID |
+----+---------+--------+
| 1 | 1 | 1,2,3 |
| 2 | 2 | 1,3 |
+----+---------+--------+
Look-Up Table
+----+---------+--------+
| ID | OrderID | ItemID |
+----+---------+--------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 1 | 3 |
| 4 | 2 | 1 |
| 4 | 2 | 3 |
+----+---------+--------+
I now want to do join that grabs all of the item names for each order but I don't want "Parent Items" if a "Child Item" exists (e.g. if the order has Clothes and Shirts I don't want Clothes.
I am not sure why this Select/Delete query is challenging me so as I've done a number of them but it is escaping me for some reason.
Any Joins I do, even if I join either/both the Item Types table or the Look-up Table twice get me only:
The Single Child Record
Select L.ID,S.ID,S.ParentID fromLook-up TableL
Inner Join Orders O
On L.OrderID=O.ID
Inner JoinItem TypesS
On L.ItemID=S.ID
Inner JoinItem Types S2
On S.ParentID=S2.ID`
Both the Parent Records
Perhaps I've been working on this way too long because the answer seems obvious and I've done plenty of these this week but somehow this one is escaping me.
When SqlFiddle is back-up I will post one to make this a lot easier to debug, any thoughts in the interim appreciate.

Selecting multiple columns from previous row in MySQL

Suppose I have a table like this:
| id | date | name | value |
|----|------------|------|-------|
| 0 | 2017-01-14 | foo | one |
| 1 | 2017-01-17 | bar | two |
| 2 | 2017-01-18 | john | five |
| 3 | 2017-01-19 | doe | ten |
(where date need not necessarily be ordered)
I want to be able to select some values of the previous row (based on date). Such a functionality can be achieved by the following query:
SELECT
*,
(SELECT
name
FROM
example e2
WHERE
e2.dt < e1.dt
ORDER BY dt DESC
LIMIT 1
) as prev_name
FROM example e1
with resulting table:
| id | dt | name | value | prev_name |
|----|------------|------|-------|-----------|
| 0 | 2017-01-14 | foo | one | (null) |
| 1 | 2017-01-17 | bar | two | foo |
| 2 | 2017-01-18 | john | five | bar |
| 3 | 2017-01-19 | doe | ten | john |
Now, this works just fine. However, it would be preferable if I could easily select multiple columns from the previous row, resulting in a result like:
| id | dt | name | value | prev_name | prev_value | prev_dt |
|----|------------|------|-------|-----------|------------|------------|
| 0 | 2017-01-14 | foo | one | (null) | (null) | (null) |
| 1 | 2017-01-17 | bar | two | foo | one | 2017-01-14 |
| 2 | 2017-01-18 | john | five | bar | two | 2017-01-17 |
| 3 | 2017-01-19 | doe | ten | john | five | 2017-01-18 |
This can of course be accomplished by simply copying the subquery (SELECT [..] FROM example e2 ...) into the query multiple times, but I guess this is not the preferable way to go. I have found several question on SO addressing either the "how to select records from a previous row" or the "how to select multiple columns using subqueries" problem, but not both. The latter problem is then mostly solved by using a JOIN statement, but I think this is not combinable with the "previous row" case. So my question is: what would be a better way to produce the last result, rather then copying a subquery for every column we need?
EDIT. As an extra constraint, that I did not include in the original question, "previous" could actually be something different from the previous row, but rather "the previous row that satisfies a condition". So suppose my table contains an extra boolean column b
| id | dt | name | value | b |
|----|------------|------|-------|---|
| 0 | 2017-01-14 | foo | one | 1 |
| 1 | 2017-01-17 | bar | two | 0 |
| 2 | 2017-01-18 | john | five | 1 |
| 3 | 2017-01-19 | doe | ten | 0 |
I would want the "previous row" to be the previous row with b = 1, so the desired result would be:
| id | dt | name | value | b | prev_name | prev_value | prev_dt |
|----|------------|------|-------|---|-----------|------------|------------|
| 0 | 2017-01-14 | foo | one | 1 | (null) | (null) | (null) |
| 1 | 2017-01-17 | bar | two | 0 | foo | one | 2017-01-14 |
| 2 | 2017-01-18 | john | five | 1 | foo | one | 2017-01-14 |
| 3 | 2017-01-19 | doe | ten | 0 | john | five | 2017-01-18 |
I think this can still be accomplished by James Scott's answer, by simply only updating the variables when b = 1, using an IF-statement, but maybe there is another solution possible in this case.
EDIT. SQLfiddle
Something like this will return the id of the 'previous' row.
SELECT x.*
, MAX(y.id) prev_id
FROM example x
LEFT
JOIN example y
ON y.id < x.id
AND y.b = 1
GROUP
BY x.id;
I'll leave the business of returning the rest of the data associated with this row as an exercise for the reader.
Looks like a good use case for session variables if you only want the previous row, you can use ORDER BY to get different results.
SET #VDt := NULL, #VName := NULL, #VValue := NULL;
SELECT id, #VName prev_name, #VValue prev_value, #VDt prev_dt, #VDt := dt dt, #VName := `name` `name`, #VValue := `value` `value` FROM example;
Messed this up when I first posted, note that the variables must be set after they are returned from the previous row. To reorder the columns (if desired) you can wrap this query in another that then reorders the result columns.
Let me know if you need anything else,
Regards,
James

mysql request using join and group_concat

I have three tables "names", "groups", "tasks"
"names"
| nid | name |
| 1 | John |
| 2 | Jim |
| 3 | Jerry |
"groups"
| gid | nid |
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 2 | 3 |
"tasks"
| tid | gid |
| 1 | 2 |
| 2 | 1 |
I want to get a list of names, that belong to a task, like this:
| tid | names |
| 1 | Jim,Jerry |
| 2 | John,Jim |
I successfully tried:
SELECT t.tid,n.name
FROM tasks t
LEFT JOIN groups g ON g.gid=t.gid
LEFT JOIN names n ON n.nid=g.nid
This works so far, but the names are in single outputs so I tried to use GROUP_CONCAT(), but this does not work. I just get the first task with the wrong names!?!?
SELECT t.tid,GROUP_CONCAT(n.name)
FROM tasks t
LEFT JOIN groups g ON g.gid=t.gid
LEFT JOIN names n ON n.nid=g.nid
I have never used GROUP_CONCAT() before. It works well, when using it in a single SELECT without joins. A short explanation what I did wrong, would be nice.
When using group_concat you need to group your results.
Add group by t.id at the end of your query.

Query Peformance for Tags

I’m creating a system, backed by a MySQL database, and a question appeared about what would be the best practice.
I have a “group“ entity, which have zero or more "tags".
The tables could be represented by:
Groups
+-------+--------+
| id | name |
+-------+--------+
| 1 | Group1 |
| 2 | Hey Guy|
| 3 | Chacko |
| 4 | Dropo |
+-------+--------+
GroupsTags
+-------+--------+
|idGroup| idTag |
+-------+--------+
| 1 | 1 |
| 1 | 2 |
| 2 | 1 |
+-------+--------+
Tags
+-------+--------+
| id | name |
+-------+--------+
| 1 | Health |
| 2 | Happy |
+-------+--------+
The system, during the “group” registration, I want that to autocomplete every key press, showing the most popular tags, similar to StackOverflow. In other words, I have to do a query at database for each letter type, order by popularity.
What solution is the best?
What is the best?
select count(*) as qt,Tags.nome from GroupsTags
inner join Tags
on Tags.id = GroupsTags.idTag
where Tags.name like "phrase%"
group by GroupsTags.idTag
order by qt desc
or, save in the Tags table, the amount of times it was used. Like this
Tags
+-------+--------+--------+
| id | name | qtUsed |
+-------+--------+--------+
| 1 | Health | 2 |
| 2 | Happy | 1 |
+-------+--------+--------+
select * from Tags where name like "phrase%" order by qtUsed desc
I wonder if it is really needed to put the number of times that the tag was used to get a better performance. Would it be a bad practice ?

Trouble writing MySQL query involving ordered data from three tables

I have a table called tags, like this:
+-------+----------+
| tagID | tagName |
+-------+----------+
| 1 | jewelery |
| 2 | gifts |
| 3 | asdf |
| 4 | fashion |
| 5 | diamonds |
+-------+----------+
Then a table called coupon_tags, like this:
+-------+----------+
| tagID | couponID |
+-------+----------+
| 1 | 1 |
| 2 | 1 |
| 3 | 2 |
| 4 | 2 |
| 5 | 3 |
+-------+----------+
And lastly, a table called coupons, here are the pertinent parts (id is the same as couponID elsewhere):
+----+-----------------+
| id | zone |
+----+-----------------+
| 1 | Los Angeles |
| 2 | Orange County |
| 3 | Los Angeles |
| 5 | Orange County |
| 6 | Orange County |
+----+-----------------+
What I need to write a query for: I want to get tagNames via the first table that correspond to the ordered list of the top 10 most used tagIDs in the second table, but it only looks through couponIDs that match another criteria - that the "zone" be a certain zone. In the end, only the top 10 tagNames from a certain zone will show. I've never done a triple-table query before, any help?
I'm trying to keep this purely SQL, as I had a partially working PHP solution but it was messy and very slow.
SELECT tags.tagName FROM
(SELECT tagID, COUNT(*) FROM
coupon_tags
JOIN coupons ON coupons.couponID = coupon_tags.couponID AND zone = 'Los Angeles'
GROUP BY tagID ORDER BY COUNT(*) DESC LIMIT 10) AS most_used
JOIN tags ON most_used.tagID = tags.tagID