from code loop to Sql query? - mysql

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.

Related

MYSQL Sorting By Multiple Join Children

I cannot seem to figure out a way to sort sql queries by joined children.
Database Example:
Table: posts
+------+---------+
| id | title |
+------+---------+
| 0 |'title1' |
| 1 |'title2' |
| 2 |'title3' |
+------+---------+
Table: post_meta
+------+---------+----------+---------+
| id | post_id | key | value |
+------+---------+----------+---------+
| 0 |0 |'coolness'| 5 |
| 1 |0 |'desc' |'random' |
| 2 |0 |'author' |'bill' |
| 3 |1 |'coolness'| 2 |
| 4 |1 |'desc ' |'random' |
| 5 |2 |'author' |'joe' |
| 6 |2 |'coolness'| 9 |
+------+---------+----------+---------+
I want a list of posts (or just post ids) ordered by their 'coolness' meta number (asc or desc). I dont know if I should be selecting from the posts table and joining on the meta table, or vise-versa. When I join on the post_meta table I only get data from one of the meta rows, so if I just add an order by post_meta.coolness nothing happens.
Thanks!
If you just want the id, you can use the post_meta table:
select pm.post_id
from post_meta pm
where pm.key = 'coolness'
order by pm.value + 0;
The + 0 is to convert the value (presumably a string) to a number.
If you need other columns related to the post, you can join in the posts table.

Rewriting a select query

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.

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

Combining a LEFT JOIN with GROUP BY, whilst also including NULLs

I'm having trouble combining tables that have a one-to-many mapping using LEFT JOIN and GROUP BY.
I have the following table with a unique ID (in the illustrative example this is house_number)
Houses:
|house_number| bedrooms|
|0 | 4 |
|1 | 3 |
|2 | 1 |
And I want to LEFT JOIN with a second table USING the unique ID, where the second table may or may not have multiple entries per unique ID. E.g,
Occupants:
| house_number | occupant_id | type |
| 0 | 3 | 19 |
| 0 | 1 | 20 |
| 0 | 2 | 21 |
| 2 | 7 | 20 |
Now what I want to achieve is exactly ONE entry per house number, but giving a preference in the LEFT JOIN to occupants with a type of 20, whilst also keeping those houses which do not have any occupants listed, e.g,
|house_number| bedrooms| occupant_id | type |
|0 | 4 | 1 | 20 |
|1 | 3 | null | null |
|2 | 1 | 7 | 20 |
I can use a GROUP BY to achieve only one entry per house, however, I need to ensure that the occupant row returned with it (if it exists) has type = 20.
If I simply use a WHERE (type = 20), then I wouldn't get an entry returned for house_number = 1.
How would I achieve this final table?
What about trying WHERE (type = 20 OR type is null) condition instead?
SELECT h.house_number,h.bedrooms
, o.occupant_id,o.ztype
FROM houses h
LEFT JOIN occupants o ON h.house_number = o.house_number
AND o.ztype =20
;
BTW I had to replace "type" by "ztype" because type is a reserved word in Postgres.

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.