Update multiple rows, but only first row with different value - mysql

table
create table tst(locationId int,
scheduleCount tinyint(1) DEFAULT 0,
displayFlag tinyint(1) DEFAULT 0);
INSERT INTO tst(locationId,scheduleCount)
values(5,0),(2,0),(5,1),(5,2),(2,1),(2,2);
I update multiple rows and multiple columns with one query, but want to change the one of the columns only for the first row and keep the other things the same for that column.
I want to update all the rows with some location id and change displayFlag to 1 and increment scheduleCount of only the top entry with 1 , rest would remain the same
**Query **
update tst,(select #rownum:=0) r,
set tst.displayFlag =1,
scheduleCount = (CASE WHEN #rownum=0
then scheduleCount+1
ELSE scheduleCount
END),
#rownum:=1 where locationId = 5
But it gives error and does not set the user defined variable rownum, I am able to join the tables in a select and change the value of the rownum, is there any other way to update the values.

I'm not sure this is the correct way of doing such a thing, but it is possible to include the user variable logic in the CASE condition:
UPDATE tst
JOIN (SELECT #first_row := 1) r
SET tst.displayFlag = 1,
scheduleCount = CASE
WHEN #first_row = 1 AND ((#first_row := 0) OR TRUE) THEN scheduleCount+1
ELSE scheduleCount
END
WHERE locationId = 5;
I have used a #first_row flag as this is more inline with your initial attempt.
The CASE works as follows:
On the first row #first_row = 1 so the second part of the WHEN after AND is processed, setting #first_row := 0. Unfortunately for us, the assignment returns 0, hence the OR TRUE to ensure the condition as a whole is TRUE. Thus scheduleCount + 1 is used.
On the second row #first_row != 1 so the condition is FALSE, the second part of the WHEN after AND is not processed and the ELSE scheduleCount is used.
You can see it working in this SQL Fiddle. Note; I have had to set the column types to TINYINT(3) to get the correct results.
N.B. Without an ORDER BY there is no guarantee as to what the '1st' row will be; not even that it will be the 1st as returned by a SELECT * FROM tst.
UPDATE
Unfortunately one cannot add an ORDER BY if there is a join.. so you have a choice:
Initialise #first_row outside the query and remove the JOIN.
Otherwise you are probably better off rewriting the query to something similar to:
UPDATE tst
JOIN (
SELECT locationId,
scheduleCount,
displayFlag,
#row_number := #row_number + 1 AS row_number
FROM tst
JOIN (SELECT #row_number := 0) init
WHERE locationId = 5
ORDER BY scheduleCount DESC
) tst2
ON tst2.locationId = tst.locationId
AND tst2.scheduleCount = tst.scheduleCount
AND tst2.displayFlag = tst.displayFlag
SET tst.displayFlag = 1,
tst.scheduleCount = CASE
WHEN tst2.row_number = 1 THEN tst.scheduleCount+1
ELSE tst.scheduleCount
END;
Or write two queries:
UPDATE tst
SET displayFlag = 1
WHERE locationId = 5;
UPDATE tst
SET scheduleCount = scheduleCount + 1
WHERE locationId = 5
ORDER BY scheduleCount DESC
LIMIT 1;

Related

UPDATE from SELECT but still return selected data in MySQL

I have read a bunch of ways that has gotten me this far. But I can't get to the finish line.
I have a table of coupon codes. I want to use one transaction to select the next available code, mark it as used and input the order number. I can get the update and nested select to work, but I cannot figure out how to actually return the coupon code from the select. It just returns 1 row updated.
Here's what I've got:
UPDATE `prcoupon` pr
SET
`pr`.`status` = '1',
`pr`.`invoicenumber` = '09990002'
WHERE
`pr`.`couponCode` = (SELECT
`prcoupon`.`couponcode`
FROM
`prcoupon`
WHERE
`status` = 0
LIMIT 1)
Sample data
What I need returned is: couponCode: SL2T-03A0-JVCY-W2XMXG
If I understand correctly, you can try to use UPDATE ... JOIN with ROW_Nunber windwon function.
UPDATE prcoupon pr
JOIN (
SELECT *,ROW_NUMBER() OVER(ORDER BY couponCode) rn
FROM prcoupon
WHERE status = 0
) t2 ON pr.couponcode = t2.couponcode
SET pr.status = 1,
pr.invoicenumber = '09990002'
WHERE rn = 1
sqlfiddle

sql rank row results

I"m trying to add a new col that shows the rank (or sequence) of row results by date.
I've written:
SELECT
#row_number:=(CASE
WHEN #member_id = lh.member_id and lc.ladder_advocacy is not null
THEN #row_number + 1
when #member_id = lh.member_id and lc.ladder_advocacy is null then "null"
ELSE 1 /* there is an error here - i need it to return a 1 if not null, then 2 for the 2nd instance, etc */
END) AS rank_advocacy,
#member_id:=lh.member_id AS member_id,
lh.ladder_change,
lc.name,
lc.ladder_advocacy,
lc.ladder_elected,
lc.ladder_policy,
lc.ladder_engagement,
lc.ladder_newventure,
lc.ladder_collective,
lc.is_trigger
FROM
leenk_ladder_history AS lh
LEFT JOIN
leeds_so.leenk_ladder_config AS lc ON lh.ladder_config_id = lc.id
WHERE
ladder_change = 1 AND trigger_active = 1
ORDER BY member_id, trigger_event_date DESC;
There is an error at row 4, and I'm not sure how to fix it. For the first result, I want to return 1. for the second results, I want to return #row_number + 1. Third result, #row_number+2 (etc).
How do I achieve this?
I don't understand how the condition lc.ladder_advocacy is not null is being used. However, the basic structure is:
SELECT (#row_number = IF(#member_id = lh.member_id, #row_number + 1
IF(#member_id := lh.member_id, 1, 1)
)
) as rank_advocacy,
lh.ladder_change,
. . .
Some really important points:
You need to assign #member_id and #row_number in the same expression. MySQL (as with all other databases) does not guarantee the order of evaluation of expressions.
In more recent versions of MySQL, I think the ORDER BY needs to go in a subquery, with the variable expressions in the outer query.

Create one alias virtual colum containing value of either of two columns

See the SQL query below:
SELECT *
FROM
(SELECT
h.hotel_id AS id,h.hotel_city AS city,h.hotel_name AS hotelname,
h.hotel_star AS hotelcat, hwd.double_spl_rate, hwd.third_party_rate,
hwd.extra_bed_spl_rate, hwd.meal_plan_spl,
hwd.third_party_extra_bed, hwd.third_party_meal_plan,
hwd.room_category, hrd.hotel_rate_from, hrd.hotel_rate_to
FROM
hotels_list AS h
INNER JOIN
hotel_rate_detail AS hrd ON h.hotel_id = hrd.hotels_id
INNER JOIN
hotel_week_days AS hwd ON hrd.hotel_id = hwd.h_id
WHERE
(('2015-07-31' BETWEEN hrd.hotel_rate_from AND hrd.hotel_rate_to)
OR
('2015-08-01' BETWEEN hrd.hotel_rate_from AND hrd.hotel_rate_to)
)
AND (h.hotel_city = '1')
AND (hwd.double_spl_rate != 0 OR hwd.third_party_rate != 0)
AND (h.hotel_star = '4')
ORDER BY
hwd.double_spl_rate, hwd.third_party_rate ASC) AS result_table
GROUP BY
result_table.id
ORDER BY
result_table.double_spl_rate, result_table.third_party_rate ASC
LIMIT 0,5;
OUTPUT is attached below:
In the above output there are two columns double_spl_rate and third_party_rate which can be either 0 or a value greater than zero.
How can I create a virtual column alias which only contain values greater the zero. Let us suppose the column is final_rate which will contain values as
id | final_rate
533 | 3776
9228 | 3000
Yes, you can do this like so:
select
id,
case
when coalesce(double_spl_rate,0) = 0
then third_party_rate
else double_spl_rate
end as final_rate
from table
The coalesce operator will set double_spl_rate to 0 if it's null, and the case expression will return third_party_rate if double_spl_rate is 0.
If double_spl_rate cannot be null you can skip the coalesce part.
Note that the code above will always prefer the value in double_spl_rate and disregard the other value if both values are greater than 0. If you don't want this you could extend the logic in the case expression to account for that and return the sum of the values instead. Or you could simply just return third_party_rate + double_spl_rate in all cases.

Trying to merge multiple UPDATE statements into one within SQL stored procedure

I have multiple update statements in a stored procedure (as shown below).
Question is I am trying to combine them into one UPDATE statement as there is a performance issue (takes longer to execute stored procedure). I tried putting columns (such as PONUMBER, VENDORID etc) in a single update statement but it is throwing errors.
Please suggest.
UPDATE rptMaster SET PONUMBER = (select top 1 poMaster.PONUMBER from poMaster where poMaster.ITEMNMBR =rptMaster.ITEMNMBR and
poMaster.UnCommited > 0)
UPDATE rptMaster SET VENDORID = (select top 1 poMaster.VENDORID from poMaster where poMaster.ITEMNMBR =rptMaster.ITEMNMBR and
poMaster.UnCommited > 0)
UPDATE rptMaster SET DUEDATE = (select top 1 poMaster.REQDATE from poMaster where poMaster.ITEMNMBR =rptMaster.ITEMNMBR and
poMaster.UnCommited > 0)
UPDATE rptMaster SET POQTYORDER = (select top 1 (poMaster.QTYORDER / rptMaster.UOMQTY) from poMaster where poMaster.ITEMNMBR =rptMaster.ITEMNMBR and
poMaster.UnCommited > 0)
Mine is similar to polkduran's:
WITH PO AS (
SELECT PONUMBER
, VENDORID
, REQDATE
, QTYORDER
, ITEMNMBR
, ROW_NUMBER() OVER (PARTITION BY ITEMNMBR ORDER BY ??) as RN
FROM poMaster
WHERE UnCommited > 0
)
UPDATE rptMaster
SET PONUMBER = po.PONUMBER
, VENDORID = po.VENDORID
, DUEDATE = po.REQDATE
, POQTYORDER = po.QTYORDER / rptMaster.UOMQTY
FROM rptMaster
JOIN PO
ON PO.ITEMNMBR = rptMaster.ITEMNMBR
and PO.RN = 1
I'm using a Common Table Expression (CTE) to assign a row number to each poMaster record, with the records for each value of ITEMNMBR numbered separately. This allows us to pick to the first record for each ITEMNBR in our JOIN, later, similar to the way you were using Top 1 in your subqueries.
Please note, though: because you didn't indicate how you wanted to select the Top 1 record in your query, I had to leave the ORDER BY clause in the CTE unspecified. (I put ?? in as a placeholder.) You need to specify one or more sort fields in place of the ?? so it knows how to sort and number the records.
You can make an update using a join clause:
update rpt
set
PONUMBER = po.PONUMBER,
VENDORID = po.VENDORID,
DUEDATE = po.REQDATE,
POQTYORDER = (po.QTYORDER / rpt.UOMQTY)
from rptMaster rpt
inner join poMaster po
on po.ITEMNMBR = rpt.ITEMNMBR
where po.UnCommited > 0
I don't have a way to test it right now but that might work.

using a variable in MYSQL limits in subquery

I'm trying to recreate some code I did in SQL server on mySQL. I want to insert a row for every row in a table. I am using a loop to do this, in SQL server I used SELECT TOP #foo
here is my mySQl
begin
set #maxloop = (select max(id) from `LeagueInfo`);
set #loopno = 1;
while #loopno <= #maxloop DO
SET #mtop = (select `teams` * `homegames` from `LeagueInfo` where id = #loopno);
SET #div = (select `LeagueShortName` from `LeagueInfo` where id = #loopno);
SET #teams = (select teams from `LeagueInfo` where id = #loopno);
SET #homegames = (select homegames from `LeagueInfo` where id = #loopno);
SET #fthgsum = (select sum(`FTHG`)/#teams/#homegames from `footy` where `id` in(select`id`, `div` from `footy`
where `div` = #DIV
order by `matchdate` desc LIMIT #mtop));
SET #ftagsum = (select sum(`FTAG`)/#teams/#homegames from `footy` where `id` in(select`id`, `div` from `footy`
where `div` = #DIV
order by `matchdate` desc LIMIT #mtop));
insert into `looptable` (`di`, `homeav`, `awayav`) values (#div, #fthgsum,#fthgsum);
set #loopno = #loopno +1;
END while;
END;
I get an error on limit #mtop. I've read that I can get round this with a prepared statement but I'm not sure how to do it in a subquery. Is there a was to do this or is there another was I can select the top x amount of rows based on a value in another table for each row in that table.
Thanks
Paul
In your line of code,
SET #mtop = (select 'teams' * 'homegames' from 'LeagueInfo' where id = #loopno)
mtop is not a number, which is false since limit needs to be a number.
maybe you want to use "count(*)"?