Set of data that needs to be sorted with column 1 showing on the first record but not remaining records. I could add another column with boolean to determine which is first record. The desired result below.
+--------+------------+-------+
| type | variety | price |
+--------+------------+-------+
| apple | gala | 2.79 |
| | fuji | 0.24 |
| | limbertwig | 2.87 |
| orange | valencia | 3.59 |
| | navel | 9.36 |
| pear | bradford | 6.05 |
| | bartlett | 2.14 |
| cherry | bing | 2.55 |
| | chelan | 6.33 |
+--------+------------+-------+
SELECT `type`, `variety`, `price`
FROM (
SELECT IF(#prev != t.`type`, t.`type`, '') AS `type`
, t.`variety`, t.`price`
, #prev := t.`type` AS actualType
FROM theTable AS t
CROSS JOIN (SELECT #prev := '') AS init
ORDER BY t.`type`, t.`variety`, t.`price`
) AS subQ
It's been a while since I did something like this, but this is the general idea.
The init subquery is just used to initialize the #prev session variable.
The IF uses the "last seen" type to determine whether to show the type.
The expression aliased as actualType updates the #prev session variable for the next row processed.
The ORDER BY is needed to order the rows so that the #prev works out appropriately; in some cases I've had to put the ORDER BY in a deeper subquery (SELECT ... FROM theTable ORDER BY ...) AS t to make sure it is not applied after the expressions involving #prev.
As others have mentioned, this is best done within the client as this is an issue of presentation, but you can technically achieve what you are looking for using the ROW_NUMBER windowed function and a CASE statement. I don't have a MYSQL instance handy, but the following should work.
WITH T as (
SELECT
ROW_NUMBER() OVER ( PARTITION BY type ORDER BY 1 ) rownum,
type,
variety,
price
FROM products )
SELECT
CASE WHEN rownum = 1 THEN type ELSE '' END type,
variety,
price
FROM t;
Related
I'm pretty lost with all the SQL stuff, and I'd like to know how to implement a field that gives the player a rank depending of the level and the score he set.
I'm not that good with mySQL so that's why I'd like to know how should I do this.
For MySql 8.0 you can use rank():
select
t.*,
rank() over (partition by t.level order by t.score DESC) `rank`
from tablename t
order by level, user
See the demo.
For earlier versions:
select
t.*,
(select count(*) from tablename where level = t.level and score > t.score) + 1 `rank`
from tablename t
See the demo.
Results:
| level | user | score | rank |
| ----- | ---- | ----- | ---- |
| 1 | A | 10 | 3 |
| 1 | B | 15 | 2 |
| 1 | C | 30 | 1 |
| 2 | A | 20 | 2 |
| 2 | B | 10 | 3 |
| 2 | C | 40 | 1 |
Calculated values are not something that you should be storing in the tables. Similar exmple is age. You do not store the age of the user, but its DOB. Values that keep changing are a complex issue when discussing database modeling.
There is a name for this type of a problem. It is called a slowly changing dimensions wiki-link. Therefore, I would advise you to implement any calculation logic in you application, rather then having it stored in the database.
But, if for some reason you must have it on your DB level, you can always create a procedure that gets executed at certain periods of the day. However, this might create situations that your data in the tables might not be accurate until the time that procedure gets executed introduction to procedures.
try this:
select level,user,score,
rank() over(partition by level order by score desc) as rank
from Tablename;
Post the problem statement and current code I am using, and wondering if any smart ideas to improve query performance? Using MySQL. Thanks.
Write a SQL query to rank scores. If there is a tie between two scores, both should have the same ranking. Note that after a tie, the next ranking number should be the next consecutive integer value. In other words, there should be no "holes" between ranks.
+----+-------+
| Id | Score |
+----+-------+
| 1 | 3.50 |
| 2 | 3.65 |
| 3 | 4.00 |
| 4 | 3.85 |
| 5 | 4.00 |
| 6 | 3.65 |
+----+-------+
For example, given the above Scores table, your query should generate the following report (order by highest score):
+-------+------+
| Score | Rank |
+-------+------+
| 4.00 | 1 |
| 4.00 | 1 |
| 3.85 | 2 |
| 3.65 | 3 |
| 3.65 | 3 |
| 3.50 | 4 |
+-------+------+
SELECT
s.score, scores_and_ranks.rank
FROM
Scores s
JOIN
(
SELECT
score_primary.score, COUNT(DISTINCT score_higher.score) + 1 AS rank
FROM
Scores score_primary
LEFT JOIN Scores score_higher
ON score_higher.score > score_primary.score
GROUP BY score_primary.score
) scores_and_ranks
ON s.score = scores_and_ranks.score
ORDER BY rank ASC;
BTW, post issue from Gordon's code.
BTW, tried sgeddes's code, but met with new issues,
New issue from Gordon's code,
thanks in advance,
Lin
User defined variables are probably faster than what you are doing. However, you need to be careful when using them. In particular, you cannot assign a variable in one expression and use it in another -- I mean, you can, but the expressions can be evaluated in any order so your code may not do what you intend.
So, you need to do all the work in a single expression:
select s.*,
(#rn := if(#s = score, #rn,
if(#s := score, #rn + 1, #rn + 1)
)
) as rank
from scores s cross join
(select #rn := 0, #s := 0) params
order by score desc;
One option is to use user-defined variables:
select score,
#rnk:=if(#prevScore=score,#rnk,#rnk+1) rnk,
#prevScore:=score
from scores
join (select #rnk:=0, #prevScore:=0) t
order by score desc
SQL Fiddle Demo
I have data which is formed like this:
+----------+-------+-------+
| DAY | VALUE | Name |
+----------+-------+-------+
| 01/01/14 | 1030 | BOB
| 01/02/14 | 1020 | BOB
| 01/03/14 | 1080 | BOB
| 01/04/14 | 1090 | BOB
| 01/05/14 | 1040 | BOB
| 01/08/14 | 1030 | BOB
| 01/11/14 | 4030 | BOB
| 01/12/14 | 5000 | BOB
| 01/13/14 | 6000 | BOB
| 01/14/14 | 1096 | BOB
| 01/14/14 | 1200 | MIKE
| 01/15/14 | 1040 | MIKE
| 01/16/14 | 1600 | MIKE
| 01/17/14 | 1070 | MIKE
| 01/18/14 | 1340 | MIKE
| 01/19/14 | 1060 | MIKE
| 01/01/14 | 6000 | JANE
| 01/02/14 | 1700 | JANE
| 01/03/14 | 1070 | JANE
| 01/04/14 | 8000 | JANE
+----------+-------+------+
For each name there needs to be a row for the dates between 01/01/14 to 02/01/14 (1 month). As you can see Bob, Mike, and Jane (although in my real database there are thousands of names) are all missing dates between this time period. I would like to somehow insert the missing rows by interpolation of some sort. For example Bob is missing 01/06/14 and 01/07/14. I would like it to interpolate by adding these two dates and then the values to be the average of the two field between so these two missing fields would both have the value ((1040+1030)/2) = 1035. If there is no data before like for MIKE (starts at 01/14/14) I would like all the new rows to have 01/14/14 value now. I have tried various different techniques such as using coalesce command, cursors, but can't get it to work. Also I am not set on having these EXACT values, if there is some sort of math library which can interpolate I would be open to this as well. Thanks.
You have two problems, generating the rows and interpolating the values. You can generate the rows with this SQL:
select d.day, n.name, t.value
from (select distinct name from table t) n cross join
(select distinct day from table t) d left outer join
table t
on t.name = n.name and t.day = d.day;
Doing the interpolation is troublesome. You can do this using variables and multiple sorting. Here is logic:
select day, name, value, prev_value,
#value as next_value,
#value := if(#name = name and value is not null, value, #value),
#name := name
from (select d.day, n.name, t.value,
#value as prev_value,
#value := if(#name = name and value is not null, value, #value),
#name := name
from (select distinct name from table t) n cross join
(select distinct day from table t) d left outer join
table t
on t.name = n.name and t.day = d.day cross join
(select #name := '', #value := NULL) vars
order by n.name, d.day
) t cross join
(select #name := '', #value := NULL) vars
order by n.name, d.day desc;
This will probably work for you, but it is depending on MySQL evaluating the expressions in order in each select (for the assignment of variables). You can make the syntax more complicated to fix this, but that would hide the logic. You can now implement the logic that you want:
select day, name,
(case when value is not null then value
when prev_value is not null and next_value is not null
then (prev_value + next_value) / 2
when prev_value is null then next_value
else prev_value
end) as value
from (<previous query here>) t;
Assuming that we have the following MySQL table:
ID | Name | Last_Name | Location |
1 | Alex | Griff | DT |
2 | John | Doe | York |
3 | Pat | Benat | DT |
4 | Jack | Darny | DT |
5 | Duff | Hill | York |
I want to create an sql statement that selects randomly one row of each location and store them in a new table.
For example:
2 | John | Doe | York |
3 | Pat | Benat | DT |
OR
4 | Jack | Darny | DT |
5 | Duff | Hill | York |
I would like to execute this on SQL since it's much faster than doing it on a Java program and using HashMap<K,V> and then storing the values again in another table.
This page has the solution. I simply modified the query to your table definition, as follows:
SELECT tmp.ID, tmp.Name, tmp.Last_Name, tmp.Location
FROM profiles
LEFT JOIN (SELECT * FROM profiles ORDER BY RAND()) tmp ON (profiles.Location = tmp.Location)
GROUP BY tmp.Location
ORDER BY profiles.Location;
SQL Fiddle demo
If you want a random sample for each location, you have several options. I think the easiest is a variable approach that will work well if your table is not super big.
select t.*
from (select t.*,
#rn := if(#location = location, #rn + 1, 1) as rn,
#location := location
from table t cross join
(select #location := '', #rn := 0) vars
order by location, rand()
) t
where rn = 1;
This assigns a sequential number to the locations and then chooses the first one.
Trying to sort rows from lowest to highest continually, or rather repeatedly using MySql. For example: if a column has the following values: 1,3,2,4,2,1,4,3,5, then it should end up like this 1,2,3,4,5,1,2,3,4. So it goes from lowest to highest, but tries to sort again from lowest to highest multiple times.
For large sets, the semi-JOIN operation (the approach in the answer from Strawberry) may create an unwieldy resultset. (Then again, MySQL may have some optimizations in there.)
Another alternative available in MySQL is to use "user variables", like this:
SELECT r.mycol
FROM ( SELECT IF(q.mycol=#prev,#seq := #seq + 1,#seq := 1) AS seq
, #prev := q.mycol AS mycol
FROM mytable q
JOIN (SELECT #prev := NULL, #seq := NULL) p
ORDER BY q.mycol
) r
ORDER BY r.seq, r.mycol
Let me unpack that a bit, and explain what it's doing, starting with the inner query (inline view aliased as r.) We're telling MySQL to get the column (mycol) containing the values you want to sort, e.g. 1,3,2,4,2,1,4,3,5 and we're telling MySQL to order these in ascending sequence: 1,1,2,2,3,3,4,4,5.
The "trick" now is to use a MySQL user variable, so that we can compare the mycol value from the current row to the mycol value from the previous row, and we use that to assign an ascending sequence value, from 1..n on each distinct value.
With that resultset, we can tell MySQL to order by that assigned sequence value first, and then by the value from mycol.
If there is a unique id on each row, then a correlated subquery can be used to get an equivalent result (although this approach is very unlikely to perform as well on large sets)
SELECT r.mycol
FROM mytable r
ORDER
BY ( SELECT COUNT(1)
FROM mytable q
WHERE q.mycol = r.mycol
AND q.id <= r.id
)
, r.mycol
Here's the setup for the test case:
CREATE TABLE mytable (id INT, mycol INT);
INSERT INTO mytable (id, mycol) VALUES
(1,1),(2,3),(3,2),(4,4),(5,2),(6,1),(7,4),(8,3),(9,5);
there is no order two time just this
ORDER BY column ASC
Let's pretend that the PK is a unique integer. Consider the following...
CREATE TABLE seq(id INT NOT NULL PRIMARY KEY,val INT);
INSERT INTO seq VALUES (8,1),(4,2),(1,3),(2,4),(7,0),(6,1),(3,2),(5,5);
SELECT * FROM seq ORDER BY val;
+----+------+
| id | val |
+----+------+
| 7 | 0 |
| 6 | 1 |
| 8 | 1 |
| 3 | 2 |
| 4 | 2 |
| 1 | 3 |
| 2 | 4 |
| 5 | 5 |
+----+------+
SELECT x.*
, COUNT(*) rank
FROM seq x
JOIN seq y
ON y.val = x.val
AND y.id <= x.id
GROUP
BY id
ORDER
BY rank
, val;
+----+------+------+
| id | val | rank |
+----+------+------+
| 7 | 0 | 1 |
| 6 | 1 | 1 |
| 3 | 2 | 1 |
| 1 | 3 | 1 |
| 2 | 4 | 1 |
| 5 | 5 | 1 |
| 8 | 1 | 2 |
| 4 | 2 | 2 |
+----+------+------+