This question is asked by my friend. He faced this question during an interview. Please forget about db structure :), I think they asked this question to check logic :)
Here is the scenario. There is a hotel with different floors and different room numbers in each room.
Initially table will be like this;
Floor Rooms
1
1
1
2
2
Rooms column will have null values
We need to fill as shown below using a single query. Is this possible? If so how we can do that. Please advice.
Floor Rooms
1 101
1 102
1 103
2 201
2 202
Very simple,Code for SQL Server,
with cte as
(
select *
,ROW_NUMBER() over (partition by fno order by fno) as rowNo
from hotel
)
update cte set rNo = fno*100 + rowNo
DEMO
Code of MySQL, I was able to select only, for update Temp table should be used, I am new to MYSQL.
set #type = 1;
set #num = 0;
select fno,(fno*100) + row_number as newRommNo from
(
select *
,#num := if(#type = fno, #num + 1, 1) as row_number
,#type :=fno
from hotel
) as a
DEMO OF MYSQL
Use this query if you're looking for a true atomic solution without having to set and reset session variables outside of your update query:
UPDATE floor_rooms
INNER JOIN (
SELECT DISTINCT floor, (#room := 0), (#floor := 0)
FROM floor_rooms
WHERE ISNULL(room)
) AS floors
ON floor_rooms.floor = floors.floor
SET floor_rooms.room =
IF(#floor = floor_rooms.floor, #room := (#room + 1), #room := 1)
+ (#floor := floor_rooms.floor) * 100;
DEMO # SQL Fiddle
Try this one
update tablename set `Rooms`=(Floor*100)+1
This is may be help for you
we must declare scalar variable:
SELECT fno, CASE WHEN fno = 1 THEN fno * 100 +#rooms :=#rooms + 1
WHEN fno = 2 THEN fno * 100 +#room :=#room + 1 END FROM hotel,
(SELECT #rooms := 0) AS w1, (SELECT #room := 0) AS w2
here demo : http://sqlfiddle.com/#!2/54c0d/1
Related
Is it possible to get specific row in query using like SUM?
Example:
id tickets
1 10 1-10 10=10
2 35 11-45 10+35=45
3 45 46-90 10+35+45=90
4 110 91-200 10+35+45+110=200
Total: 200 tickets(In SUM), I need to get row ID who have ticket with number like 23(Output would be ID: 2, because ID: 2 contains 11-45tickets in SUM)
You can do it by defining a local variable into your select query (in form clause), e.g.:
select id, #total := #total + tickets as seats
from test, (select #total := 0) t
Here is the SQL Fiddle.
You seem to want the row where "23" fits in. I think this does the trick:
select t.*
from (select t.*, (#total := #total + tickets) as running_total
from t cross join
(select #total := 0) params
order by id
) t
where 23 > running_total - tickets and 23 <= running_total;
SELECT
d.id
,d.tickets
,CONCAT(
TRIM(CAST(d.RunningTotal - d.tickets + 1 AS CHAR(10)))
,'-'
,TRIM(CAST(d.RunningTotal AS CHAR(10)))
) as TicketRange
,d.RunningTotal
FROM
(
SELECT
id
,tickets
,#total := #total + tickets as RunningTotal
FROM
test
CROSS JOIN (select #total := 0) var
ORDER BY
id
) d
This is similar to Darshan's answer but there are a few key differences:
You shouldn't use implicit join syntax, explicit join has more functionality in the long run and has been a standard for more than 20 years
ORDER BY will make a huge difference on your running total when calculated with a variable! if you change the order it will calculate differently so you need to consider how you want to do the running total, by date? by id? by??? and make sure you put it in the query.
finally I actually calculated the range as well.
And here is how you can do it without using variables:
SELECT
d.id
,d.tickets
,CONCAT(
TRIM(d.LowRange)
,'-'
,TRIM(
CAST(RunningTotal AS CHAR(10))
)
) as TicketRange
,d.RunningTotal
FROM
(
SELECT
t.id
,t.tickets
,CAST(COALESCE(SUM(t2.tickets),0) + 1 AS CHAR(10)) as LowRange
,t.tickets + COALESCE(SUM(t2.tickets),0) as RunningTotal
FROM
test t
LEFT JOIN test t2
ON t.id > t2. id
GROUP BY
t.id
,t.tickets
) d
I am using a modified version of a query similiar to another question here:Convert SQL Server query to MySQL
Select *
from
(
SELECT tbl.*, #counter := #counter +1 counter
FROM (select #counter:=0) initvar, tbl
Where client_id = 55
ORDER BY ordcolumn
) X
where counter >= (80/100 * #counter);
ORDER BY ordcolumn
tbl.* contains the field 'client_id' and I am attempting to get the top 20% of the records for each client_id in a single statement. Right now if I feed it a single client_id in the where statement it gives me the correct results, however if I feed it multiple client_id's it simply takes the top 20% of the combined recordset instead of doing each client_id individually.
I'm aware of how to do this in most databases, but the logic in MySQL is eluding me. I get the feeling it involves some ranking and partitioning.
Sample data is pretty straight forward.
Client_id rate
1 1
1 2
1 3
(etc to rate = 100)
2 1
2 2
2 3
(etc to rate = 100)
Actual values aren't that clean, but it works.
As an added bonus...there is also a date field associated to these records and 1 to 100 exists for this client for multiple dates. I need to grab the top 20% of records for each client_id, year(date),month(date)
You need to do the enumeration for each client:
SELECT *
FROM (SELECT tbl.*, #counter := #counter +1 counter
(#rn := if(#c = client_id, #rn + 1,
if(#c := client_id, 1, 1)
)
)
FROM (select #c := -1, #rn := 0) initvar CROSS JOIN tbl
ORDER BY client_id, ordcolumn
) t cross join
(SELECT client_id, COUNT(*) as cnt
FROM tbl
GROUP BY client_id
) tt
where rn >= (80/100 * tt.cnt);
ORDER BY ordcolumn;
Using Gordon's answer as a starting point, I think this might be closer to what you need.
SELECT t.*
, (#counter := #counter+1) AS overallRow
, (#clientRow := if(#prevClient = t.client_id, #clientRow + 1,
if(#prevClient := t.client_id, 1, 1) -- This just updates #prevClient without creating an extra field, though it makes it a little harder to read
)
) AS clientRow
-- Alteratively (for everything done in clientRow)
, #clientRow := if(#prevClient = t.client_id, #clientRow + 1, 1) AS clientRow
, #prevClient := t.client_id AS extraField
-- This may be more reliable as well; I not sure if the order
-- of evaluation of IF(,,) is reliable enough to guarantee
-- no side effects in the non-"alternatively" clientRow calculation.
FROM tbl AS t
INNER JOIN (
SELECT client_id, COUNT(*) AS c
FROM tbl
GROUP BY client_id
) AS cc ON tbl.client_id = cc.client_id
INNER JOIN (select #prevClient := -1, #clientRow := 0) AS initvar ON 1 = 1
WHERE t.client_id = 55
HAVING clientRow * 5 < cc.c -- You can use a HAVING without a GROUP BY in MySQL
-- (note that clientRow is derived, so you cannot use it in the `WHERE`)
ORDER BY t.client_id, t.ordcolumn
;
I have a quite complex query to get some data from the database, sort them and rank them accordingly.
Here is the SQL fiddle for it: SQL Fiddle
Now what I want to do is, to add a WHERE statement to this query, so only limited users will be selected (3 users above and 3 users below, the id = 8).
WHERE sort BETWEEN #userpos - 3 AND #userpos + 3
So it should look something like this, but with the first example:
SQL Fiddle
I have already tried to implement this WHERE statement to this query, but I couldn't figure it out where should I add, as I've always received error (that the column cannot be found).
Any suggestion and / or solution for my problem? Should I rewrite the whole query for this?
If I understand correctly, you can do this with a subquery:
SET #userid = 8
SELECT *
FROM (SELECT #pos := #pos + 1 AS sort, points, r.userid, s.active
FROM rank r JOIN
settings s
USING (userid) CROSS JOIN
(SELECT #pos := 0) p
WHERE s.active = 1
ORDER BY points DESC
) list
WHERE userid = #userid;
Note that this eliminates a layer of subqueries that you have. Otherwise, it is quite similar to your query.
EDIT:
The above was based more on the SQL Fiddle than on the question. (Oops.)
To get three rows before and after a given row is possible and just a small tweak, using a trick. The trick is to define another variable with the user pos and then use that variable in the outer query:
SELECT *
FROM (SELECT #pos := #pos + 1 AS sort, points, r.userid, s.active,
if(userid = #userid, #userpos := #pos, 0)
FROM rank r JOIN
settings s
USING (userid) CROSS JOIN
(SELECT #pos := 0, #userpos := 0) p
WHERE s.active = 1
ORDER BY points DESC
) list
WHERE `sort` between #userpos - 3 and #userpos + 3;
Note: MySQL does not guarantee the order of evaluation for variables in the select. The following is a bit safer in terms of order of execution:
SELECT *
FROM (SELECT (case when (#pos := #pos + 1) is NULL then NULL
when (case when (userid = #userid) then #userpos := #pos else 1 end) is null
then NULL
else #pos
end) AS sort, points, r.userid, s.active,
if(userid = #userid, #userpos := #pos)
FROM rank r JOIN
settings s
USING (userid) CROSS JOIN
(SELECT #pos := 0, #userpos := 0) p
WHERE s.active = 1
ORDER BY points DESC
) list
WHERE `sort` between #userpos - 3 and #userpos + 3;
The weird case statements are to ensure statement executions. The is null is to ensure that the when clauses fail, so the assignments are made sequentially.
Couldn't really explain my problem with words, but with an example I can show it clearly:
I have a table like this:
id num val
0 3 10
1 5 12
2 7 12
3 11 15
And I want to go through all the rows, and calculate the increase of the "num", and multiply that difference with the "val" value. And when I calculated all of these, I want to add these results together.
This is the mathematical equation, that I want to run on the table:
Result = (3-0)*10 + (5-3)*12 + (7-5)*12 + (11-7)*15
138 = Result
Thank you.
You can do with mysql variables, but you will still get one record for each entry.
select
#lastTotal := #lastTotal + ( (yt.num - #lastNum) * yt.val ) thisLineTotal,
#lastNum := yt.num as saveForNextRow,
yt.id
from
yourTable yt,
( select #lastTotal := 0,
#lastNum := 0 ) sqlvars
order by
id
This SHOULD give you what you want to confirm the calculations to each record basis.
Now, to get the one record and one column result, you can wrap it such as
select
pq.thisLineTotal
from
(above entire query ) as pq
order by
pq.id DESC
limit 1
Assuming the IDs are consecutive as your sample data suggests, just join the table to itself:
select sum((t1.num-ifnull(t2.num,0))*t1.val) YourValue
from YourTable t1
left join YourTable t2
on t2.id = t1.id - 1;
http://www.sqlfiddle.com/#!2/40b9f/12
This will give you the total. Make sure to order in the order you wish - I have ordered by id
SET #runtot:=0;
SET #prevval:=0;
select max(rt) as total FROM (
SELECT
q.val,
q.num,
(#runtot := #runtot + (q.num- #prevval) * q.val) AS rt,
(#prevval := q.num) AS pv
FROM thetable q
ORDER by ID) tot
If you want to see the details of the calculation, leave out the outer select as so:
SET #runtot:=0;
SET #prevval:=0;
SELECT
q.val,
q.num,
(#runtot := #runtot + (q.num- #prevval) * q.val) AS rt,
(#prevval := q.num) AS pv
FROM thetable q
ORDER by ID
If it is possible to have negative numbers for your column values, using max(rt) won't work for the total. You should then use:
SET #runtot:=0;
SET #prevval:=0;
select #runtot as total FROM (
SELECT
q.val,
q.num,
(#runtot := #runtot + (q.num- #prevval) * q.val) AS rt,
(#prevval := q.num) AS pv
FROM thetable q
ORDER by ID) tot LIMIT 1
Considering following table that doesn't have any primary key, can I select every other row?
col1 col2
2 a
1 b
3 c
12 g
first select must find: 2, 3
second select must find: 1, 12
is that possible?
In unique MySQL fashion:
select *
from (
select *
, #rn := #rn + 1 as rn
from Table1
join (select #rn := 0) i
) s
where rn mod 2 = 0 -- Use = 1 for the other set
Example at SQL Fiddle.
Try this. I've adapted it from the answer linked below.
I tested it on SQLFiddle and it appears to work.
http://sqlfiddle.com/#!2/0bccf/28
http://sqlfiddle.com/#!2/0bccf/29
Odd Rows:
SELECT x.*
FROM (
SELECT #rownum:=#rownum+1 rownum, t.*
FROM (SELECT #rownum:=0) r, table t
) x
WHERE MOD(x.rownum, 2) = 1
Even Rows:
SELECT x.*
FROM (
SELECT #rownum:=#rownum+1 rownum, t.*
FROM (SELECT #rownum:=0) r, table t
) x
WHERE MOD(x.rownum, 2) = 0
Adapted from:
MySQL row number
yes possible using temp variable
Example :
set #a := 0;
select * from car_m_city WHERE mod((#a:=#a+1), 2) = 1
Explanation :
here in sql we declare #a( set #a := 0;) temp variable.(#a:=#a+1) now #a increment by 1.jsut like simple way to check odd or even
mod((#a:=#a+1), 2) = 1 for odd data
mod((#a:=#a+1), 2) = 0 for even data
This works for me.
SET #row_number = 0;
select* from (
SELECT
(#row_number:=#row_number + 1) AS num, col1,col2
FROM
TABLE1
) as t WHERE num%2=0
You can use mod 1 for odd or mod 0 for even rows
This should work for MySQL:
SELECT col1, col2
FROM (
SELECT col1, col2, #rowNumber:=#rowNumber+ 1 rn
FROM YourTable
JOIN (SELECT #rowNumber:= 0) r
) t
WHERE rn % 2 = 1
This uses % which is the MOD operator.
And here is the sample fiddle: http://sqlfiddle.com/#!2/cd31b/2