Rewriting a select query - mysql

I have a rather simple (I think) question at hand. The example tables and the result I need are provided below (in reality those tables containt much more columns and data, I jest left what is relevant). There is also the query which returns exactly what I need. However, I dont like rather crude way in which it works (I dont like subqueries in general). The question is, how can I rewrite the query so it will automatically react to more columns appearing in TABLE2 in the future? Right now if the "z" column would be added to TABLE2, I need to modify each query in the code and add one more relevant subquery. I just want the select to read the entire content of TABLE2 and translate the id numbers to corresponding strings from TABLE1.
TABLE1
-----------------
id |x |
-----------------
567 |AAA |
345 |BBB |
341 |CCC |
827 |DDD |
632 |EEE |
503 |FFF |
945 |GGG |
234 |HHH |
764 |III |
123 |JJJ |
-----------------
TABLE2
-------------------------
id |x |y |
-------------------------
1 |123 |341 |
2 |567 |632 |
3 |345 |945 |
4 |764 |503 |
5 |234 |827 |
-------------------------
THE RESULT I NEED
-----------------
A |B |
-----------------
JJJ |CCC |
AAA |EEE |
BBB |GGG |
III |FFF |
HHH |DDD |
-----------------
The query I have
SELECT
(SELECT `x` FROM `TABLE1` WHERE `TABLE2`.`x` LIKE `TABLE1`.`id` LIMIT 1) as A,
(SELECT `x` FROM `TABLE1` WHERE `TABLE2`.`y` LIKE `TABLE1`.`id` LIMIT 1) as B
FROM `TABLE2` ORDER BY `id` DESC;

You might want to restructure your data model:
Instead of:
-------------------------
id |x |y |
-------------------------
1 |123 |341 |
2 |567 |632 |
3 |345 |945 |
4 |764 |503 |
5 |234 |827 |
-------------------------
You would have:
----------------------
col_id |col |
----------------------
1 |x |
2 |y |
----------------------
---------------------------
id |col_id |col_val |
---------------------------
1 |1 |123 |
1 |2 |341 |
2 |1 |567 |
2 |2 |632 |
etc
---------------------------
Probably not worth the hassle (you would effectively need to pivot when you're accessing multiple columns at a time) but it would allow you to do the query that you want across all current and future columns.

You can't do that with a plain select.
What you can do is creating a view with the translated values. You still have to modify the view when the original table is changed but your queries don't have to.

You can use dynamic sql statements, but still you can use the dynamic statements only if you are sure that table 2 will have the columns of same type like x and y(Apart from id).
Let me know if you are not sure how to write it.
All the best.

Related

Mysql - Compare int field with comma separated field from another table

I have two tables in a MySQL database like this:
User:
userid |userid | Username | Plan(VARCHAR) | Status |
-----------+------------+--------------+---------------+---------+
1 | 1 | John | 1,2,3 |1 |
2 | 2 | Cynthia | 1,2 |1 |
3 | 3 | Charles | 2,3,4 |1 |
Plan: (planid is primary key)
planid(INT) | Plan_Name | Cost | status |
-------------+----------------+----------+--------------+
1 | Tamil Pack | 100 | ACTIVE |
2 | English Pack | 100 | ACTIVE |
3 | SportsPack | 100 | ACTIVE |
4 | KidsPack | 100 | ACTIVE |
OUTPUT
id |userid | Username | Plan | Planname |
---+-------+----------+------------+-------------------------------------+
1 | 1 | John | 1,2,3 |Tamil Pack,English Pack,SportsPack |
2 | 2 | Cynthia | 1,2 |Tamil Pack,English Pack |
3 | 3 | Charles | 2,3,4 |English Pack,Sportspack, Kidspack |
Since plan id in Plan table is integer and the user can hold many plans, its stored as comma separated as varchar, so when i try with IN condition its not working.
SELECT * FROM plan WHERE find_in_set(plan_id,(select user.planid from user where user.userid=1))
This get me the 3 rows from plan table but i want the desired output as above.
How to do that.? any help Please
A rewrite off your query what should work is as follows..
Query
SELECT
all columns you need
, GROUP_CONCAT(Plan.Plan_Name ORDER BY Plan.planid) AS Planname
FROM
Plan
WHERE
FIND_IN_SET(Plan.plan_id,(
SELECT
User.Plan
FROM
user
WHERE User.userid = 1
)
)
GROUP BY
all columns what are in the select (NOT the GROUP_CONCAT function)
You also can use FIND_IN_SET on the ON clause off a INNER JOIN.
One problem is that the join won't ever use indexes.
Query
SELECT
all columns you need
, GROUP_CONCAT(Plan.Plan_Name ORDER BY Plan.planid) AS Planname
FROM
User
INNER JOIN
Plan
ON
FIND_IN_SET(Plan.id, User.Plan)
WHERE
User.id = 1
GROUP BY
all columns what are in the select (NOT the GROUP_CONCAT function)
Like i said in the comments you should normalize the table structures and add the table User_Plan whats holds the relations between the table User and Plan.

Use result of a rails where query for another query

I'm curious if there is an easy way to archive this query:
SomeTable.where(value_b: SomeTable.where(value_a: 1).pluck(:value_b))
For an example, if we have:
SomeTable
|id | a | b |
|1 | 1 | 2 |
|2 | 2 | 2 |
|3 | 3 | 3 |
|4 | 4 | 4 |
And the user enter number 1 into the system, I want to be able to find value of b where value of a = 1, which is 2. Then use this value of b to find all the records with this value.
So, I want to be able to use 1 and extract SomeTable with id: 1 and 2
Thanks!
SomeTable.joins("JOIN some_table as replica on replica.value_b = some_table.value_b")
.where(replica: {value_a: 1})
SomeTable.where("b in (SELECT b FROM some_tables where a = ?)", a_value)
a_value is your 'a' value to find 'b' value

from code loop to Sql query?

Edit:
I edit the post, to add some complexity. What if the box would contain repeated barcodes? Barcode 222222222222 exists twice in the box. And in the main table (or the warehouse) exists 3 times.
How would be the query so I delete only 2 barcodes?
The answer I chose groups the barcodes of the box, thus, only deletes one of each.
Hello all,
I have this table
Barcodes
+--+------------------+-------+
|ID|Barcode |DELETE |
+--+------------------+-------+
|1 | 11111111111111 | 0 |
|2 | 22222222222222 | 0 |
|3 | 22222222222222 | 0 |
|4 | 22222222222222 | 0 |
|5 | 33333333333333 | 0 |
|6 | 33333333333333 | 0 |
|7 | 33333333333333 | 0 |
|8 | 44444444444444 | 0 |
+--+------------------+-------+
ID is an autoincremental field.
Each line is a barcode which represents 1 unit of the same item. For example, I have 3 items represented by barcode 33333333333333
Everytime any user scans a barcode, and it matches, I mark the barcode to be deleted when the process is finished.
update barcodes
set delete=-1
where id=(select max(barcode) as maxi from barcodes where barcode like 'x' and delete>-1)
This works fairly fast. But sometimes, instead of scanning the items one by one, the user may scan a box which contains up to 150 items.
The back-end is a mysql table in a remote server, and the front-end is ms access.
BOX
+------------------+
|Barcode |
+------------------+
| 22222222222222 |
| 22222222222222 |
| 33333333333333 |
| 44444444444444 |
+------------------+
I use a loop, and for each item in the box, I repeat the sql query, but this takes up to 3 minutes for the 150 items box because of latency.
Do you know any kind of query that would update my barcodes tables with those in the read box?
The output of it would be something like:
+--+------------------+-------+
|ID|Barcode |DELETE |
+--+------------------+-------+
|1 | 11111111111111 | 0 |
|2 | 22222222222222 | -1 |
|3 | 22222222222222 | 0 |
|4 | 33333333333333 | -1 |
|5 | 33333333333333 | 0 |
|6 | 33333333333333 | 0 |
|7 | 44444444444444 | -1 |
+--+------------------+-------+
If the sale is finished, I would just delete those marked as delete=-1
What is the datatype of barcode? Avoid grouping to speed up updates. Note you are not deleting anything despite use of a column with that name. Remove like and > in the sql and replace them with = logic with something like the following for mysql server side code:
update barcodes
set delete=-1
where id=(select id from barcodes where barcode='x' and delete=0 ORDER BY id DESC LIMIT 1)
You'll have to adjust the above to suit the specifics of x in your example.
You can use the following query to pick the id values of the rows to be deleted:
SELECT MIN(id)
FROM Barcodes AS b1
INNER JOIN BOX AS b2 ON b1.Barcode = b2.Barcode
WHERE `DELETE` <> -1
GROUP BY b1.Barcode
If you use the above SELECT query in your original UPDATE query, then you have what you need:
UPDATE Barcodes
SET `DELETE` = - 1
WHERE id IN ( SELECT minID
FROM (SELECT MIN(id) AS minID
FROM Barcodes AS b1
INNER JOIN BOX AS b2 ON b1.Barcode = b2.Barcode
WHERE `DELETE` <> -1
GROUP BY b1.Barcode) AS t )
The above works in MySQL. See demo here.
Note that you need to wrap the SELECT query in a sub-query, since MySQL doen't let you specify target table 'Barcodes' for update in FROM clause.

Extending Values in Joined Tables with MYSQL

I currently have a table of concentrations, which are linked to a table of elements. There is a concentration table
|conc_id|element_id|conc|
and and element table
|element_id|symbol|
The concentration table only has element_ids for each conc_id that have non-zero concentrations.
What I am trying to do is create a query which will, for each concentration_id, list all the elements in order, with their concentration, regardless of whether they're non-zero or not. I have tried this in a number of increasingly complicated ways (starting with a RIGHT JOIN) but this always outputs a NULL conc_id when there is none of that element in it. The output I am looking for is something like this:
|conc_id|element_id|symbol|conc|
|1 |1 |H |1.2 |
|1 |2 |He |NULL|
|1 |3 |Li |2.3 |
...
|3 |1 |H |4.5 |
|3 |2 |He |NULL|
|3 |3 |Li |NULL|
...
And so on. Is there a way I can do this without having NULL conc_id?
Thanks for any help in advance...
First, you have to build a table that contains all combinations between conc_id and element_id:
SELECT DISTINCT conc_id, elements.element_id FROM concentrations, elements;
Which gives you the following table:
| conc_id | element_id |
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
...
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
...
Once you have such a table, joining it with concentrations and elements should be fairly simple. For example:
SELECT combinations.conc_id, combinations.element_id, symbol, conc
FROM
(SELECT DISTINCT conc_id, elements.element_id FROM concentrations, elements) AS combinations
LEFT JOIN concentrations ON (combinations.conc_id = concentrations.conc_id AND combinations.element_id = concentrations.element_id)
LEFT JOIN elements ON (combinations.element_id = elements.element_id);
Result:
| conc_id | element_id | symbol | conc |
| 1 | 1 | H | 1.2 |
| 1 | 2 | He | NULL |
| 1 | 3 | Li | 2.3 |
...
| 3 | 1 | H | 4.5 |
| 3 | 2 | He | NULL |
| 3 | 3 | Li | NULL |
...
To be honest, I think the best way would be to actually fill in the missing data with zero values in the conc column. Otherwise, you're trying to hack it to display data that you don't actually have and it would be much more tangible than keeping null values anyway...
Assuming there are no performance consideration that this would impede, of course.
Assuming you do not have a simple list of all the conc_id somewhere, you could try something like:
SELECT c.conc_id, e.element_id, e.symbol, c2.conc
FROM
(SELECT DISTINCT conc_id FROM concentration c) c
INNER JOIN element e
LEFT JOIN concentration c2 ON c2.conc_id = c.conc_id AND c2.element_id = e.element_id
ORDER BY c.conc_id, e.element_id
The logic is:
Get a list of all unique conc_id derived table c
Do a cross join against element (for each row of c, list all row of element)
Left join against the full concentration table.
If you have another table with all the conc_id, you could avoid the derived table entirely.
select conc_id, conc.element_id, symbol, concentration from concentration_table as conc
join element_table as elem on conc.element_id = elem.element_id
order by conc.conc_id asc
This gives me this table:
conc_id element_id concentration element_id symbol
1 1 1.2 1 H
1 2 0 2 He
1 3 2.3 3 Li
3 1 4.5 1 H
3 2 0 2 He
3 3 0 3 Li
And these are the starting tables:
element_table:
element_id symbol
1 H
2 He
3 Li
concentration table:
conc_id element_id concentration
1 1 1.2
1 2 0
1 3 2.3
3 1 4.5
3 2 0
3 3 0
EDIT: edited the query to obtain correct table values requested

SQL: Compare if column values in two tables are equal

I'm working on mysql and have two tables with the same schema:
preTrial
|id|accusedId|articleid|
------------------------
|1 | 1 | 1 |
|2 | 1 | 2 |
|3 | 1 | 3 |
|4 | 2 | 1 |
|5 | 2 | 2 |
trial
|id|accusedId|articleid|
------------------------
|1 | 1 | 1 |
|2 | 1 | 2 |
|3 | 2 | 1 |
|4 | 2 | 2 |
I want to get those accusedIds where all the articleIds of the first and the second tables are equal.
The above example should only return the accusedId 2, cause for accusedId 1 there is no articleId 3 in the second table.
I hope you understand what i mean. I'm currently writing my thesis in law, and the the time i was into sql is long gone by. Of course i already did some research, and tried several joins, but i was not able to find a solution. Hopefully you can help me.
Try something like this:
select a.accusedId , sum(a.accusedid) as cnt_a, sum(coalesce(b.accusedId, 0)) as cnt_b
from a left join b on a.accusedId = b.accusedId and a.articleId = b.articleId
group by accusedId
having cnt_a = cnt_b
I haven't even run that, so it might be a little off, but give it a lash. What it's doing is returning zeroes for a row in a not matched by b, so the HAVING clause will filter your grouped results to those where the article counts are equal.