Numbering group results into unique identifiers - sql-server-2008

I am trying to create an query that results in unique identifiers (account number).
What I need to achieve is for each unique entry into Col1 to create a another row number / UI.
I have been attempting this with the below query
elect col1,col2 ,(DENSE_RANK() OVER( ORDER BY col1,col2)) as UI from [TABLE]
and this is what i have been getting:
col1 col2 UI
34 1 1
1973 448 2
355 3924 3
18709 8168 4
5201 9211 5
5762 9294 6
3864 10669 7
4914 12568 8
4914 12569 9
42465 921 10
but i need it to look like this:
col1 col2 UI
34 1 1
1973 448 2
355 3924 3
18709 8168 4
5201 9211 5
5762 9294 6
3864 10669 7
4914 12568 8
4914 12569 8
42465 921 9

You need to define a way to - more loosely - rank col2 e.g. using division
select
col1,col2
,DENSE_RANK() OVER( ORDER BY col1,col2/10) as newUI
from mytable
+----+-------+-------+-------+
| | col1 | col2 | newUI |
+----+-------+-------+-------+
| 1 | 34 | 1 | 1 |
| 2 | 355 | 3924 | 2 |
| 3 | 1973 | 448 | 3 |
| 4 | 3864 | 10669 | 4 |
| 5 | 4914 | 12568 | 5 |
| 6 | 4914 | 12569 | 5 |
| 7 | 5201 | 9211 | 6 |
| 8 | 5762 | 9294 | 7 |
| 9 | 18709 | 8168 | 8 |
| 10 | 42465 | 921 | 9 |
+----+-------+-------+-------+

Related

MySQL CTE - is it possible to iterate over range of numbers?

I'm curious if is that possible to do similar query using CTE (the values of "id" may vary, not necessarily in succession)
SELECT
elt((#mxId := if(#mxId + 1 <= 3, #mxId + 1, 1)), 5, 10, 22, 33) val,
id
FROM my_table
INNER JOIN (SELECT #mxId := 0) tmp;
Expected output:
val | id
-----+----
5 | 1
10 | 2
22 | 3
5 | 4
10 | 5
22 | 6
5 | 7
10 | 8
22 | 9
5 | 10
10 | 11
22 | 12
5 | 13
10 | 14
22 | 15
5 | 16
10 | 17
22 | 18
5 | 19
10 | 20
with my_table_numbered as (
select ((row_number() over ())-1)%3 as rn_mod, id from my_table
)
select elt(rn_mod+1, 5, 10, 22, 33) as val, id
from my_table_numbered;
Output:
+------+----+
| val | id |
+------+----+
| 5 | 1 |
| 10 | 2 |
| 22 | 3 |
| 5 | 4 |
| 10 | 6 |
| 22 | 7 |
| 5 | 8 |
| 10 | 9 |
| 22 | 13 |
| 5 | 14 |
| 10 | 15 |
| 22 | 16 |
+------+----+
You should specify an ORDER BY in the CTE to get a meaningful and reliable row numbering, but you didn't have one in the query in your question.
row_number() OVER ()
that's do the magic - Thanks very much

Filtering Column based on values on Another table

I'am tying to get the specific columns whose name starts with some patterns.
table_1
abcA| abcB | abcC | xyD | mnE
1 | 2 | 3 | 4 | 5
6 | 7 | 8 | 9 | 10
11 | 12 | 13 | 14 | 15
From the above table i'am in need of the Output Like
abcA | abcB | abcC
1 | 2 | 3
6 | 7 | 8
11 | 12 | 13
The columns should be selected DYNAMICALLY by filtering like any column name starts with abc should me selected.
I Tried this Query
"select column_name from information_schema.columns
where table_name='table_1' and column_name like 'abc%';"
It gives a another table only with column names
column_name
abcA
abcB
abcC
But I want to get the values of that Column names.
Thanks
This is poor table design, and it is fairly difficult to write code which can select a dynamic column name. Here is the design I would suggest to you:
ID | name | pos
1 | abcA | 1
2 | abcB | 1
3 | abcC | 1
4 | xyD | 1
5 | mnE | 1
6 | abcA | 2
7 | abcB | 2
8 | abcC | 2
9 | xyD | 2
10 | mnE | 2
11 | abcA | 3
12 | abcB | 3
13 | abcC | 3
14 | xyD | 3
15 | mnE | 3
With this design in place, you only need a very simple query:
SELECT pos, GROUP_CONCAT(ID) AS ids
FROM yourTable
WHERE name LIKE 'abc%'
GROUP BY pos;

Convert MySQL table from vertical to horizontal based on id column

I have a table like below
id | main_id | image
1 | 10 | 52343.jpg
2 | 10 | 52344.jpg
3 | 10 | 52345.jpg
4 | 11 | 52346.jpg
5 | 11 | 52347.jpg
6 | 11 | 52348.jpg
7 | 11 | 52349.jpg
8 | 12 | 52350.jpg
9 | 12 | 52351.jpg
i want output like this :
id | main_id | image1
1 | 10 | 52343.jpg, 52344.jpg, 52345.jpg
2 | 11 | 52346.jpg, 52347.jpg, 52348.jpg, 52349.jpg
3 | 12 | 52350.jpg, 52351.jpg
Just all images with same main_id to be in one row.
use group_concat()
select main_id,group_concat(image)
from tablename
group by main_id

SQL Query to Sort the result according to maximum common results

I have a problem in making SQL query. I am making a small Search Engine in which the word to page mapping or indexes are kept like this.
Sorry I wasn't able to post images here so I tried writing the output like this.
+---------+---------+-----------+--------+
| word_id | page_id | frequency | degree |
+---------+---------+-----------+--------+
| 2331 | 29 | 2 | 1 |
| 2332 | 29 | 7 | 1 |
| 2333 | 29 | 4 | 1 |
| 2334 | 29 | 1 | 1 |
| 2335 | 29 | 1 | 1 |
| 2336 | 29 | 1 | 1 |
| 2337 | 29 | 2 | 1 |
| 2338 | 29 | 7 | 1 |
| 2343 | 29 | 1 | 3 |
| 2344 | 29 | 1 | 3 |
......
......
...... and so on.
Word_id points to Words present in other table and page_id points to URLs present in other table.
Now Suppose I want to search "Rapid 3D Prototyping Services". I brought the union of results corresponding to individual words by query ->
select * from words_detail where word_id=2353 or word_id=2364 or word_id=2709 or word_id=2710;
In above query the word_ids corresponds to the 4 words in the search query and the results are as below.
Union of page_id corresponding to individual words...
mysql>
select * from words_detail where word_id=2353 or word_id=2364 or word_id=2709 or word_id=2710;
+---------+---------+-----------+--------+
| word_id | page_id | frequency | degree |
+---------+---------+-----------+--------+
| 2353 | 29 | 2 | 4 |
| 2353 | 33 | 2 | 2 |
| 2353 | 36 | 5 | 9 |
| 2353 | 40 | 1 | 4 |
| 2353 | 41 | 1 | 9 |
| 2353 | 45 | 4 | 9 |
| 2353 | 47 | 2 | 9 |
| 2353 | 49 | 4 | 9 |
| 2353 | 52 | 1 | 4 |
| 2353 | 53 | 1 | 9 |
| 2353 | 66 | 2 | 9 |
| 2364 | 29 | 1 | 4 |
| 2364 | 34 | 1 | 4 |
| 2364 | 36 | 9 | 2 |
| 2709 | 36 | 1 | 9 |
| 2710 | 36 | 1 | 9 |
+---------+---------+-----------+--------+
16 rows in set (0.00 sec)
But I want the result to be sorted according to maximum match. The earlier result should be where all 4 words match, next result should be with 3 match and so on. In other words earlier results should have those page_id which are common to 4 word_ids, next should be those which are common in 3 words_ids and so on.
I checked here but this is not working in my case because in my case OR conditions are not matched in a single row.
How can such a query can be designed?
Use the occurence of you page_id as your matching count and then order by it.
select * from words_detail A
inner join
(SELECT PAGE_ID
, COUNT(PAGE_ID) matchCount
from words_detail
where word_id=2353 or word_id=2364 or word_id=2709 or word_id=2710
group by PAGE_ID) B
on A.PAGE_ID=B.PAGE_ID
where word_id=2353 or word_id=2364 or word_id=2709 or word_id=2710
order by matchCount desc
Try this
select p.*
from words_detail p
, (select word_id, count(1) as count
from words_detail where
word_id in (2353,2364,2709,2710) group by word_id) t
where p.word_id = t.word_id
order by t.count desc;
You can do a subquery to get the number of apperances for each page. Then you have to join the subquery with your table and you will be able to order the results by the number of page appearances.
Your final query should look like this:
SELECT *
FROM words_detail,
(
SELECT page_id,
COUNT(*) AS npages
FROM words_detail
WHERE word_id IN (2353, 2364, 2709, 2710)
GROUP BY page_id
) AS matches
WHERE words_detail.page_id = matches.page_id
AND word_id IN (2353, 2364, 2709, 2710)
ORDER BY matches.npages DESC

How to get this specific user rankings query in mysql?

I've got tbl_items in my user database that I want to sort user rankings on a particular item with certain id (514). I have test data on my dev environment with this set of data:
mysql> select * from tbl_items where classid=514;
+---------+---------+----------+
| ownerId | classId | quantity |
+---------+---------+----------+
| 1 | 514 | 3 |
| 2 | 514 | 5 |
| 3 | 514 | 11 |
| 4 | 514 | 46 |
| 5 | 514 | 57 |
| 6 | 514 | 6 |
| 7 | 514 | 3 |
| 8 | 514 | 27 |
| 10 | 514 | 2 |
| 11 | 514 | 73 |
| 12 | 514 | 18 |
| 13 | 514 | 31 |
+---------+---------+----------+
12 rows in set (0.00 sec)
so far so good :) I wrote the following query:
set #row=0;
select a.*, #row:=#row+1 as rank
from (select a.ownerid,a.quantity from tbl_items a
where a.classid=514) a order by quantity desc;
+---------+----------+------+
| ownerid | quantity | rank |
+---------+----------+------+
| 11 | 73 | 1 |
| 5 | 57 | 2 |
| 4 | 46 | 3 |
| 13 | 31 | 4 |
| 8 | 27 | 5 |
| 12 | 18 | 6 |
| 3 | 11 | 7 |
| 6 | 6 | 8 |
| 2 | 5 | 9 |
| 7 | 3 | 10 |
| 1 | 3 | 11 |
| 10 | 2 | 12 |
+---------+----------+------+
12 rows in set (0.00 sec)
that ranks correctly the users. However in a table with lots of records, I need to do the following:
1) be able to get small portion of the list, around where the user ranking actually resides, something that would get me the surrounding records, preserving the overall rank:
I tried to do these things with setting a user variable to the ranking of the current user and by using offset and limit, but couldn't preserve the overall ranking.
This should get me something like the following (for instance ownerId=2 and surroundings limit 5:
+---------+----------+------+
| ownerid | quantity | rank |
+---------+----------+------+
| 3 | 11 | 7 |
| 6 | 6 | 8 |
| 2 | 5 | 9 | --> ownerId=2
| 7 | 3 | 10 |
| 1 | 3 | 11 |
+---------+----------+------+
5 rows in set (0.00 sec)
2) I'd also need another query (preferably single query) that gets me the top 3 places + the ranking of particular user with certain id, preferably with a single query, no matter if he's among the top 3 places or not. I couldn't get this as well
It would look like the following (for instance ownerId=2 again):
+---------+----------+------+
| ownerid | quantity | rank |
+---------+----------+------+
| 11 | 73 | 1 |
| 5 | 57 | 2 |
| 4 | 46 | 3 |
| 2 | 5 | 9 | --> ownerId=2
+---------+----------+------+
4 rows in set (0.00 sec)
Also I'm in a bit of a concern about the performance of the queries on a table with millions of records...
Hope someone helps :)
1) 5 entries around a given id.
set #row=0;
set #rk2=-1;
set #id=2;
select b.* from (
select a.*, #row:=#row+1 as rank, if(a.ownerid=#id, #rk2:=#row, -1) as rank2
from (
select a.ownerid,a.quantity
from tbl_items a
where a.classid=514) a
order by quantity desc) b
where b.rank > #rk2 - 3
limit 5;
Though you'll get an extra column rank2: you probably want to filter it out by explicit list of columns instead of b.*. Maybe it's possible whith a having clause rather than an extra nesting.
2) 3 top ranked entries + 1 specific id
select b.* from (
select a.*, #row:=#row+1 as rank
from (
select a.ownerid,a.quantity
from tbl_items a
where a.classid=514) a
order by quantity desc) b
where b.rank < 4 or b.ownerid=#id