I need to update cells within a specific column based upon ids in another column. The column names are Prod_ID, Lang_ID and Descr:
Prod_ID | Lang_ID | Descr
--------+---------+------
A101 | 1 | TextA
A101 | 2 | TextB
A101 | 3 | TextC
For a group of rows with the same Prod_ID, I need to replace all subsequent descriptions (Descr column) with the description of the first row. The row with the correct description has always Lang_ID = 1. Also, the table may not be sorted by Lang_ID.
Example: TextA (Lang_ID = 1) should replace TextB and TextC because the Prod_IDs of the rows match.
You mentioned in a comment elsewhere that the "master" lang_id is always 1. That simplifies things greatly, and you can do this with a simple self-join (no subqueries :-)
This query selects all lang_1 rows, then joins them with all non-lang_1 rows of the same prod_id and updates those.
If Lang_ID=1 is always the "first"
UPDATE products
LEFT JOIN products as duplicates
ON products.Prod_ID=duplicates.Prod_ID
AND duplicates.Lang_ID != 1
SET duplicates.Descr = products.Descr
WHERE products.Lang_ID = 1
edit: If Lang_ID=1 may not be the "first"
you can join the table to itself via a an intermediate join which finds the lowest Lang_ID for that row. I have called the intermediate-join "lang_finder".
UPDATE products
LEFT JOIN (SELECT Prod_ID, MIN(Lang_ID) as Lang_ID FROM products GROUP BY Prod_ID) as lang_finder
ON products.prod_id=lang_finder.prod_id
LEFT JOIN products as cannonical_lang
ON products.Prod_ID = cannonical_lang.Prod_ID
AND lang_finder.Lang_ID = cannonical_lang.Lang_ID
SET products.Descr = cannonical_lang.Descr
Note that while it does use a subquery, it does not nest them. The subquery essentially just adds a column to the products table (virtually) with the value of the lowest Lang_ID, which then allows a self-join to match on that. So if there were a product with Lang_ID 3, 4, & 5, this would set the Descr on all of them to whatever was set for Lang_ID 3.
How about this?
UPDATE myTable dt1, myTable dt2
SET dt1.Descr = dt2.Descr
WHERE dt1.Prod_ID=dt2.Prod_ID;
Demo at sqlfiddle
Assuming that the correct description is always in the row of a group of rows with the same Prod_ID where Lang_ID has the smallest value, this MySQL query should work:
UPDATE your_table AS t1
JOIN (
SELECT Prod_ID, Descr
FROM (
SELECT *
FROM your_table
ORDER BY Lang_ID
) AS t3
GROUP BY Prod_ID
) AS t2
ON t1.Prod_ID = t2.Prod_ID
SET t1.Descr = t2.Descr;
The above can be used e.g. if Lang_ID is a primary or unique key. It also works if the corresponding Lang_ID has always the same minimum value (e.g. = 1) but in that case much less complex queries like this one are possible.
Related
I have the table:
id | date_submitted
1 | 01/01/2017
1 | 01/02/2017
2 | 01/03/2017
2 | 01/04/2017
I'm looking for the correct SQL to select each row, limited to one row per id that has the latest value in date_submitted.
So the SQL should return for the above table:
id | date_submitted
1 | 01/02/2017
2 | 01/04/2017
The query needs to select everything in the row, too.
Thanks for your help.
You can find max date for each id in subquery and join it with the original table to get all the rows with all the columns (assuming there are more columns apart from id and date_submitted) like this:
select t.*
from your_table t
inner join (
select id, max(date_submitted) date_submitted
from your_table
group by id
) t2 on t.id = t2.id
and t.date_submitted = t2.date_submitted;
Note that this query will return multiple rows for an id in case there are multiple rows with date_submitted equals to max date_submitted for that id. If you really want only one row per id, then the solution will be a bit different.
If you just need id and max date use:
select id, max(date_submitted) date_submitted
from your_table
group by id
I just wrote this query for my tables: NEWS and NEWS-CATEGORIES in order to count the items of each category:
SELECT DISTINCT CAT.cid, CAT.c_title, N.n_category, count(*) AS cat_count
FROM news N
inner join news - categories CAT
on CAT.cid = N.n_category
GROUP BY N.n_category
but the problem is that it just shows me the categories which contains news! but I wana get all of the categories even the ones with empty news...
my NEWS table is:
nid | n_category | etc
my NEWS-CATEGORY table is:
cid | c_title | etc
Thanks for your help
Regards
Try this:
SELECT
CAT.cid,
CAT.c_title,
count(N.n_category) AS cat_count
FROM `news-categories` CAT
LEFT JOIN `news` N
ON CAT.cid = N.n_category
GROUP BY CAT.cid,
CAT.c_title
Use LEFT JOIN:
SELECT CAT.cid, CAT.c_title, IFNULL(COUNT(N.n_category), 0) AS cat_count
FROM `news-categories` AS CAT
LEFT JOIN news AS N ON CAT.cid = N.n_category
GROUP BY CAT.cid
Things to note: 1) You have to use a column from news in the COUNT() expression, not COUNT(*), so that the null match is not counted. 2) There's no need to select N.n_category, since that's always equal to CAT.cid and you're already selecting that. 3) The GROUP BY column has to be from the news-categories table -- you can't group by a column in the table that may not have any matching rows, since that value will always be NULL.
I'm just going to point out that you can do this with a subquery as well:
SELECT CAT.cid, CAT.c_title,
(SELECT COUNT(*) FROM news N WHERE CAT.cid = N.n_category)
FROM `news - categories` CAT;
Under some circumstances, this can even have better performance.
I'm very new with SQL and need assistance on how I can accomplish this task using the correct query.
I have 2 tables that I need to use. Table "TB1" has:
id Name
1 bob
2 blow
3 joe
table "TB2" has:
compid property
1 bob
2 blow
I am trying to get which compid is missing in "TB2" and insert it from "TB1"
the query I am doing is:
SELECT id, name from TB1, TB2 where id <> compid
what I get is 2 ouputs of Id 1, and 2, and 3 outputs from id 3. by using php:
for($i=0;$i <= mysql_num_rows($comp)-1; $i++)
{
echo mysql_result($comp, $i, 0)."<br>";
}
and I expected the ouput 3 but instead got this:
1
1
2
2
3
3
3
I understand its comparing all the rows within the table but is there a way to achieve what I am looking for?
Thanks for your time.
You are performing an implicit Cartesian JOIN which results in every row against every other row. You need to specify what attribute JOINs the two tables.
Using implicit syntax (not recommended):
SELECT id, name
FROM TB1, TB2
WHERE id <> compid
AND TB1.Name = TB2.property <-- Column join
Using explicit syntax:
SELECT id, name
FROM TB1
JOIN TB2
ON TB2.property = TB1.Name <-- Column join
WHERE id <> compid
To accomplish your goal you would need something along the lines of:
SELECT TB1.id, TB1.name
FROM TB1
LEFT JOIN TB2
ON TB2.property = TB1.Name
WHERE TB2.compid IS NULL
See it in action
It's best practice to always alias the columns you select to prevent ambiguity.
To select it you can do:
SELECT *
FROM TB1
WHERE id NOT IN (
SELECT compid
FROM TB2
);
I have a table:
ID int
category int
quantity int
timestamp timestamp
I want to SELECT id="id#" and return a 'position' which is the sum of quantity for each row that has an earlier timestamp in the same category.
Thanks for your pointers!
Try this:
SELECT sum(quantity) FROM TABLE WHERE category='...' AND timestamp < some_timestamp
If you want to select by id:
SELECT sum(quantity) FROM mytable
WHERE category IN (SELECT category FROM mytable WHERE id=some_id)
AND timestamp <= some_timestamp
UPDATE
To use the timestamp from the row itself, you could do something like:
SELECT sum(quantity) FROM mytable
WHERE category IN (SELECT category FROM mytable WHERE id=some_id)
AND timestamp <= (SELECT timestamp FROM mytable WHERE id=some_id)
..or use a self-join as in the Adam's answer (maybe it wasn't an overkill, after all.. :))
UPDATE - Alternate solution
This seems to work fine too..
SELECT t1.id, sum(t2.quantity), t1.timestamp, t1.category
FROM mytable t1 INNER JOIN mytable AS t2 ON t1.category=t2.category
WHERE t1.id=some_id_here AND t2.timestamp <= t1.timestamp;
UPDATE
Changed < into <=, else the quantity for the selected item will not be counted!
You could join your table again with a subquery filtering on your criteria of same category and lesser timestamp.
Here's an example (this was done is sqlite, but it should work in mysql):
select
ID,
sum(foo2.quantity)
from
foo
left join (
select category, quantity, timestamp from foo
) as foo2 on (
foo.category = foo2.category
and foo.timestamp > foo2.timestamp
)
group by
foo.ID
Assuming your data looks like this:
ID|category|quantity|timestamp
1 |foo |2 |2011-01-01
2 |foo |1 |2011-01-02
3 |foo |4 |2011-01-03
4 |bar |4 |2011-01-03
You'll get a result like this:
ID|sum(foo2.quantity)
1 |null
2 |2
3 |3
4 |null
Note: the nulls are because there is no data for those categories prior to the given record.
Note 2: Not sure how performant this will be, but it should get you the data you are looking for.
Update
After re-reading your question, this looks to be a bit overkill... I didn't realize you were passing the id in, either way you could still use this and add a where id = "$id" to restrict it to the id are looking at... hope this helps.
SELECT * FROM table WHERE ID=(SELECT SUM(quantity) WHERE timestamp < some_timestamp AND category = (SELECT category FROM table WHERE ID='given_ID'))
If I understood correctly, this would give you the line which ID is the sum of the quantity of all the lines that have an earlier timestamp and are in the same category as the provided ID.
I didn't test it, but something along those lines should work.
If I understand your question correctly, something like this should do the trick.
$r = mysql_query("SELECT * FROM table WHERE ID='$id'");
$r = mysql_fetch_array($r);
$cat = $r['category'];
$time = $r['timestamp'];
$r = mysql_query("SELECT SUM(quantity) FROM table WHERE category='$cat' AND timestamp <= '$time'");
The first 4 lines retrieve your original id and find the category id and timestamp. Then we run a new query to get all the items in the category older than our id. Because we use <= for the timestamp, it will include our selected id's quantity. If we want to exclude our id's quantity, we would use just <
EDIT: Reading some of the other answers, you could use SUM and skip the while loop... you learn something new every day. Answer changed to reflect this.
Hello i have a question on picking random entries from a database. I have 4 tables, products, bids and autobids, and users.
Products
-------
id 20,21,22,23,24(prime_key)
price...........
etc...........
users
-------
id(prim_key)
name user1,user2,user3
etc
bids
-------
product_id
user_id
created
autobids
--------
user_id
product_id
Now a multiple users can have an autobid on an product. So for the next bidder I want to select a random user from the autobid table
example of the query in language:
for each product in the autobid table I want a random user, which is not the last bidder.
On product 20 has user1,user2,user3 an autobidding.
On product 21 has user1,user2,user3 an autobidding
Then I want a resultset that looks for example like this
20 – user2
21 – user3
Just a random user. I tried miximg the GOUP BY (product_id) and making it RAND(), but I just can't get the right values from it. Now I am getting a random user, but all the values that go with it don't match.
Can someone please help me construct this query, I am using php and mysql
The first part of the solution is concerned with identifying the latest bid for each product: these eventually wind up in temporary table "latest_bid".
Then, we assign randon rank values to each autobid for each product - excluding the latest bid for each product. We then choose the highest rank value for each product, and then output the user_id and product_id of the autobids with those highest rank values.
create temporary table lastbids (product_id int not null,
created datetime not null,
primary key( product_id, created ) );
insert into lastbids
select product_id, max(created)
from bids
group by product_id;
create temporary table latest_bid ( user_id int not null,
product_id int not null,
primary key( user_id, product_id) );
insert into latest_bid
select product_id, user_id
from bids b
join lastbids lb on lb.product_id = b.product_id and lb.created = b.created;
create temporary table rank ( user_id int not null,
product_id int not null,
rank float not null,
primary key( product_id, rank ));
# "ignore" duplicates - it should not matter
# left join on latest_bid to exclude latest_bid for each product
insert ignore into rank
select user_id, product_id, rand()
from autobids a
left join latest_bid lb on a.user_id = lb.user_id and a.product_id = lb.product_id
where lb.user_id is null;
create temporary table choice
as select product_id,max(rank) choice
from rank group by product_id;
select user_id, res.product_id from rank res
join choice on res.product_id = choice.product_id and res.rank = choice.choice;
You can use the LIMIT statement in conjunction with server-side PREPARE.
Here is an example that selects a random row from the table mysql.help_category:
select #choice:= (rand() * count(*)) from mysql.help_category;
prepare rand_msg from 'select * from mysql.help_category limit ?,1';
execute rand_msg using #choice;
deallocate prepare rand_msg;
This will need refining to prevent #choice becoming zero, but the general idea works.
Alternatively, your application can construct the count itself by running the first select, and constructing the second select with a hard-coded limit value:
select count(*) from mysql.help_category;
# application then calculates limit value and constructs the select statement:
select * from mysql.help_category limit 5,1;