I hope i'm explaining this properly... but i'm trying to update a column in a table with 30k rows with a repeated sequence.
I've populated entire columns before with random numbers using:
UPDATE locations SET template = CAST((RAND() * 4)+1 AS UNSIGNED);
Which gave:
2
4
5
1
3
etc. in a random fashion throughout the 30k rows...
I would like to enter a query that can produce a repeated sequence like:
1
2
3
4
5
1
2
3
4
5
across all 30k rows.
I've been looking into loops and auto increments but can't get it to work.
Any help much appreciated :)
Perhaps using a variable will do for example
DROP TABLE IF EXISTS T;
CREATE TABLE T(ID INT, SEQNO INT);
INSERT INTO T VALUES (1,NULL),(2,NULL),(3,NULL),(4,NULL),(5,NULL),(6,NULL),(7,NULL);
UPDATE T
SET SEQNO = (SELECT IF(#RN = 2 ,#RN:=1,#RN:=#RN + 1) FROM (SELECT #RN:=0) R)
WHERE 1 = 1
+------+-------+
| ID | SEQNO |
+------+-------+
| 1 | 1 |
| 2 | 2 |
| 3 | 1 |
| 4 | 2 |
| 5 | 1 |
| 6 | 2 |
| 7 | 1 |
+------+-------+
Thanks for the suggestions... I had a hard time finding an answer but eventually found something that would do exactly what I was after. I must admit it is far beyond my capabilities, but here it is:
SET #row_number = 0;
SET #max_num = 75;
update locations loc1
join (
select
if ((num % #max_num) = 0, #max_num, (num % #max_num)) as num2,
a.*
from (
select
(#row_number:=#row_number + 1) AS num,
loc.*
from locations loc
ORDER BY num
) a
order by num, num2
) loc2 on (loc2.id = loc1.id)
set loc1.colname = loc2.num2;
Related
I have the following table structure in a mysql database.
id | files | status
1 a.pdf,b.pdf,c.pdx 1
2 d.pdf,e.pdf.g.pdf 2
3 x.pdf,k.pdf,y.pdf 1
As you can see, the attachments are all stored on a single line.
My query is supposed to select all rows where status = 1 so i'm expecting the data in the following format.
1 1 a.pdf 1
2 1 b.pdf 1
3 1 c.pdf 1
4 3 x.pdf 1
5 3 k.pdf 1
6 3 y.pdf 1
Unfortunately, I am unsure which operator I can use to accomplish this. I'm aware SQL has pivot but i doubt even that can address my issue.
As a result, I would appreciate if I could get any help in the condition of the select query.
Regards
Try this:
SET #row_num = 0;
SELECT
(#row_num := #row_num + 1) ROW_NUM,
id,
SUBSTRING_INDEX(SUBSTRING_INDEX(files, ',', idx), ',', -1) FileName,
status
FROM
test
JOIN
(SELECT 1 idx UNION ALL SELECT 2 idx UNION ALL SELECT 3 idx UNION ALL SELECT 4 idx UNION ALL SELECT 5 idx) idxs
on idxs.idx - 2 < LENGTH(files) - LENGTH(REPLACE(files, ",", ""))
WHERE status = 1
;
Just add as many indexes as your max count of files in a string.
Note that there should be no comma fater last filename. This is magic -2 is responsible for.
Consider the following...
I have a table (ints) of integers (0-9)...
DROP TABLE IF EXISTS my_table;
CREATE TABLE my_table
(id SERIAL PRIMARY KEY
,files VARCHAR(100) NOT NULL
,status INT NOT NULL
);
INSERT INTO my_table VALUES
(1,'a.pdf,b.pdf,c.pdx',1),
(2,'d.pdf,e.pdf,g.pdf',2),
(3,'x.pdf,k.pdf,y.pdf',1);
SELECT DISTINCT id
, SUBSTRING_INDEX(SUBSTRING_INDEX(files,',',i2.i*10+i1.i+1),',',-1) file
, status
FROM my_table
, ints i1
, ints i2
WHERE status = 1
ORDER
BY id, i2.i*10+i1.i;
+----+-------+--------+
| id | file | status |
+----+-------+--------+
| 1 | a.pdf | 1 |
| 1 | b.pdf | 1 |
| 1 | c.pdx | 1 |
| 3 | x.pdf | 1 |
| 3 | k.pdf | 1 |
| 3 | y.pdf | 1 |
+----+-------+--------+
Radio_ID | Log_ID
-----------------
1 | 1
1 | 2
1 | 4
1 | 7
1 | 10
2 | 1
2 | 2
2 | 3
2 | 5
Is it possible to get the following output in a single sql statement?
Output:
Radio_ID | Log_ID
-----------------
1 | 3
1 | 5
1 | 6
1 | 8
1 | 9
2 | 4
Logic: returns missing values for each id < max value for id (ex 10 is max value for radio id 1 and missing values are 3,5,6,8,9).
I have a data parser for a radio device and when the radio is outside the coverage area it doesn't send data, so I have to send a new request for missing Log_IDs.
Thank you!
Let me assume that you have a table called numbers with integers of a sufficient range.
Then you can do:
select r.radio_id, n.n as MissingLogId
from (select radio_id, min(log_id) as minli, max(log_id) as maxli
from table t
group by radio_id
) r join
numbers n
on n.n between r.minli and r.maxli left join
table t
on t.radio_id = r.radio_id and t.log_id = n.n
where t.radio_id is null;
The idea is to generate all the numbers for each radio id (between the minimum and maximum id) using a join. Then existing values are filtered out.
If you don't have a numbers table handy, you need to create one large enough. This would probably work:
create table numbers (n int primary key);
insert into numbers(n)
select (#rn := #rn + 1) as n
from table cross join (#rn := 0);
I have a weak relation table, called header, it is basically just three ID's: id is an autoincrement primary key, did points to the id of table D and hid points to the id of table H. D and H are irrelevant here.
I want to find for any value of hid, the other values of hid that shares did with the original hid. An example:
id | did | hid
===============
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 4
6 | 2 | 5
7 | 3 | 2
8 | 3 | 6
For hid = 1 I would thus like to find id = {2,3,5,6} as those are the rows that have did in common with hid = 1.
I can do this by creating some arrays in PHP and running through all possible values of hid and respective did, but this is a quite slow process for large tables. I was wondering if there is a clever kind of JOIN or similar statement that could be used to find the cooccuring values of hid.
If I have understood you correctly:-
SELECT a.hid, GROUP_CONCAT(b.id)
FROM header a
INNER JOIN header b
ON a.did = b.did
AND b.hid != 1
WHERE a.hid = 1
GROUP BY a.hid
SQL fiddle:-
http://www.sqlfiddle.com/#!2/9aa26/1
Maybe this:
SELECT d.id
FROM (
SELECT *
FROM header
WHERE header.hid =1
) AS h
JOIN header AS d ON d.did = h.did
WHERE d.hid !=1
I need to do an advanced selection in SQL, but I'm stuck.
I have the following table:
id | user_id | position | value
1 | 1 | 1 | 1
1 | 1 | 2 | 1
1 | 1 | 3 | 3
1 | 2 | 1 | 2
1 | 2 | 2 | 2
1 | 2 | 3 | 2
1 | 3 | 1 | 3
1 | 3 | 2 | 2
1 | 3 | 3 | 1
I need a query that gives me a result set ordered as this:
Total sum for each user (user 1: 5, user 2: 6, user 3: 6)
Value for position 3 for each user (user 1: 3, user 2: 2, user 3: 1)
Val for pos 3 + val for pos 2 for each user (user 1: 4, user 2: 4, user 3: 4)
Val for pos 3 + val for pos 2 + val for pos 1 for each user (user 1: 5, user 2: 6, user 3: 6)
This is just an example, the table can actually contain more positions, so I need a query that is not hard coded on three positions.
NOTE: There is always the same number of positions for each user_id. In this example it's three, but I could as well truncate the table and add data for each user using five positions.
An ugly solution is to assume that there are never no more than ten positions, creating pos1, pos2, and so on as columns and just add them accordingly in the query. If you only use three positions you get a lot of NULL values and you also get stuck with a maximum of ten positions.
I have considered the use of temporary tables, but haven't found a breakthrough there either.
How would you do it?
I need a query that is not hard coded on three positions.
Then you can't output the subtotals in columns. SQL requires that the columns are fixed at the time you prepare the query; you can't write a query that appends more columns dynamically as it discovers how many distinct values are in the data.
You can, however, output a dynamic number of rows.
SELECT t1.user_id, CONCAT(t1.position, '-', MAX(t2.position)) AS position_range,
SUM(t2.value) AS subtotal
FROM MyTable t1
INNER JOIN MyTable t2
ON t1.user_id = t2.user_id AND t1.position <= t2.position
GROUP BY t1.user_id, t1.position;
The output is:
+---------+----------------+----------+
| user_id | position_range | subtotal |
+---------+----------------+----------+
| 1 | 1-3 | 5 |
| 1 | 2-3 | 4 |
| 1 | 3-3 | 3 |
| 2 | 1-3 | 6 |
| 2 | 2-3 | 4 |
| 2 | 3-3 | 2 |
| 3 | 1-3 | 6 |
| 3 | 2-3 | 3 |
| 3 | 3-3 | 1 |
+---------+----------------+----------+
You'll have to write application code to pivot this into columns after you fetch the whole result set.
Sorry, there is no way to write a fully dynamic pivot query in any brand of RDBMS. You have two choices:
Write code to generate the SQL based on data, as shown in #TimLehner's updated answer
Write code to post-process a general-purpose query like the one I show above.
You can potentially do something like this:
select user_id
, sum(value) as value_sum
, (select value from my_table where user_id = t.user_id and position = 3) as pos_3_val
, (select sum(value) from my_table where user_id = t.user_id and position >= 2) as pos_2_3_val
, (select sum(value) from my_table where user_id = t.user_id and position >= 1) as pos_1_2_3_val
from my_table as t
group by user_id
order by user_id
I think this should work in most any RDBMS.
If it has to by dynamic, you could potentially create this query in stored procedure or your application and run it.
You could also dynamically pivot your results from a query like this:
select *
, (
select sum(value)
from my_table
where user_id = t.user_id
and position >= t.position
) as running_total_descending
from my_table t
Please let us know if any of this works, and if you have trouble creating a dynamic version (and which RDBMS).
UPDATE
Now that we know the RDBMS (MySQL) we can have a specific dynamic version:
set #sql = null;
select
group_concat(distinct
concat(
' sum(case when position >= ',
position,
' then value end) as pos_',
position,
'_plus'
)
) into #sql
from my_table;
set #sql = concat('select user_id,', #sql, ' from my_table t group by user_id;');
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt;
SQL Fiddle
Special thanks to #bluefeet for posting this type of solution often.
I should also note that many devs believe this type of pivoting often belongs in the application or front-end. I'm no exception, both for separation of concerns and because your app can generally scale better than your OLTP database.
Suppose that I have the following two tables:
PRICE
price_id price room_id nr_of_people
1 80 1 1
2 75 1 2
3 90 2 2
4 120 3 3
ROOM
room_id room_no max_people
1 101 2
2 102 3
3 103 4
And, I need the following result:
QUERY_RESULT
price room_no nr_of_people
80 101 1
75 101 2
0 102 1
90 102 2
0 102 3
0 103 1
0 103 2
120 103 3
0 103 4
The tricky part in here is, I need to retrieve price for each people (for 2 people, for 3 people, for 4 people; that is incrementing upto the max_people defined in the room table), if there is no actual data available in the price table, it should fill in 0 by default. Above illustration should support my explanation.
I am not sure whether the table structure have any logical error.
Any thought/input/help regarding how to resolve the issue is much appreciated.
As abresas' answer and xQbert's comments suggest, you somehow need to create data in order to join it with your tables.
Like abresas' answer, I use an auxiliary table, but in my solution, this table needs to be filled with numbers 1 to N only, where N = biggest value that can ever appear on column max_people.
I created an auxiliary table called aux with a single column num. This query works for me:
SELECT IF(price.price IS NULL, 0, price.price) AS price, room.room_no, aux.num AS nr_of_people
FROM room
JOIN aux ON aux.num <= room.max_people
LEFT JOIN price ON ( price.room_id = room.room_id
AND aux.num = price.nr_of_people )
ORDER BY room.room_id, num
Unfortunately, mysql doesn't provide a native mechanism to generate a sequence of integers (see these questions), so physically creating the auxiliary table seems to be the most practical way to achieve what you need, though workarounds certainly exist if you really can't or don't want to create such table.
Just for the fun of it, the following would work without creating a new table (all inspired in the questions I linked to):
SELECT [...]
FROM room
JOIN (
SELECT 1 num
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
-- ...add as many entries as needed...
) aux ON aux.num <= room.max_people
LEFT JOIN [...]
As well as this:
SELECT [...]
FROM room
JOIN (
SELECT #row := #row +1 AS num
FROM any_table_that_is_big_enough, (SELECT #row :=0) r
) aux ON aux.num <= room.max_people
LEFT JOIN [...]
You should create a table where you have the nr_of_people from 1 up to max_people for each room, and the room_id. Then you can do an OUTER JOIN do get the information as you asked.
You can also create it as a temporary table constructing a query with the data you need in your code.
mysql> CREATE TEMPORARY TABLE nr ( nr_of_people int, room_id int );
Query OK, 0 rows affected (0.00 sec)
mysql> INSERT INTO nr VALUES ( 1, 1 ), ( 2, 1 ), ( 1, 2 ), ( 2, 2 ),
( 3, 2 ), ( 1, 3 ), ( 2, 3 ), ( 3, 3 ), ( 4, 3 );
Query OK, 9 rows affected (0.00 sec)
Records: 9 Duplicates: 0 Warnings: 0
mysql> SELECT price.price, room.room_no, nr.nr_of_people
FROM price
RIGHT OUTER JOIN nr ON price.room_id = nr.room_id AND price.nr_of_people = nr.nr_of_people
INNER JOIN room ON nr.room_id = room.room_id;
+-------+---------+--------------+
| price | room_no | nr_of_people |
+-------+---------+--------------+
| 80 | 101 | 1 |
| 75 | 101 | 2 |
| NULL | 102 | 1 |
| 90 | 102 | 2 |
| NULL | 102 | 3 |
| NULL | 103 | 1 |
| NULL | 103 | 2 |
| 120 | 103 | 3 |
| NULL | 103 | 4 |
+-------+---------+--------------+
9 rows in set (0.00 sec)