I've made an SQL query which rank pages by how many times they have been viewed. For instance,
╔══════╦═══════╗
║ PAGE ║ VIEWS ║
╠══════╬═══════╣
║ J ║ 100 ║
║ Q ║ 77 ║
║ 3 ║ 55 ║
║ A ║ 23 ║
║ 2 ║ 6 ║
╚══════╩═══════╝
Now what I would like to do is find the percentile rank of each page using an SQL query. The math I would like to use for this is simple enough, I just want to take the row number of the already generated table divided by the total number of rows. Or 1 minus this value, depending on my interests.
Can I do a COUNT(pages) on an already generated table like this? I realize that's how I will get the total number of rows. But are there any commands to return a row number?
Just to further clarify my question I need the following results
╔══════╦════════════════╗
║ PAGE ║ Percentile ║
╠══════╬════════════════╣
║ J ║ (1-1/5)*100 ║
║ Q ║ (1-2/5)*100 ║
║ 3 ║ (1-3/5)*100 ║
║ A ║ (1-4/5)*100 ║
║ 2 ║ (1-5/5)*100 ║
╚══════╩════════════════╝
Or in general (1-(row number)/(COUNT(page))*100
SELECT page,
views,
(1-ranks/totals)*100 Percentile
FROM
(
SELECT page,
views,
#rank:=#rank + 1 ranks,
(SELECT COUNT(*) FROM tableName) totals
FROM tableName a,
(SELECT #rank:=0) s
ORDER BY views DESC
) s
SQLFiddle Demo
You cannot calculate percentile ranks across a table in a single SQL statement. The approach suggested by John Woo here falls apart after the top ranks are calculated, even though the results do look good for the first (unpredictable) percent of the table being processed, meaning the top few percentiles.
The reason why is explained in this post by Oracle Ace Roland Bouman:
http://rpbouman.blogspot.com/2009/09/mysql-another-ranking-trick.html
In short: user-defined variables are not designed to be reliable within a single SQL statement, only across multiple SQL statements.
Read the first sentence of the MySQL manual about User-Defined Variables:
http://dev.mysql.com/doc/refman/5.5/en/user-variables.html
"You can store a value in a user-defined variable in one statement and then refer to it later in another statement."
Then in about the 10th paragraph see this clear statement: "As a general rule, other than in SET statements, you should never assign a value to a user variable and read the value within the same statement. [. . .] the order of evaluation for expressions involving user variables is undefined. "
Related
Table Structure
╔════╦════════════════════════════════════════════╗
║ id ║ url ║
╠════╬════════════════════════════════════════════╣
║ 1 ║ http://example.com/path1/path2/path3/name1 ║
╠════╬════════════════════════════════════════════╣
║ 2 ║ http://example.com/path1/path2/path3/name2 ║
╠════╬════════════════════════════════════════════╣
║ 3 ║ http://example.com/path1/path2/path3/name3 ║
╚════╩════════════════════════════════════════════╝
So I'm trying to truncate all url prefixes with that path (ex: http://example.com/path1/path2/path3/) so that there are names left at the end, like: name1, name2, name3.
I think this query can work fine, but I think it will change all the values in that column.
UPDATE `table` SET `url` WHERE LEFT (`url`, 37) = 'http://example.com/path1/path2/path3/'
What I want is to just cut off the front of the url-path leaving the name at the end.
I tried to use this query but the query got an error start from the LEFT after the SET query.
UPDATE `table` SET LEFT(url, 37) = '' WHERE LEFT(`url`, 37) = 'http://example.com/path1/path2/path3/'
How to achieve this?
Use simplest
UPDATE `table`
SET url = TRIM(LEADING 'http://example.com/path1/path2/path3/' FROM url)
If the value starts from the specified substring - it will be trimmed, else the value will not be altered.
I have a database table which looks like this (simplified):
╔════╦══════════════╦═════════╗
║ ID ║ Product ║ Tags ║
╠════╬══════════════╬═════════╣
║ 1 ║ Product1 ║a,1-5,b ║
║ 2 ║ Product2 ║a,6-12,d ║
║ 3 ║ Product3 ║a,20-30,c║
║ 4 ║ Product4 ║b,5-55,a ║
╚════╩══════════════╩═════════╝
The query I'm struggling with should return the results based on Tags column.
Example
Should return all products that have the a tag (no matter of the position determined with , character) with number scope from 6-21 for the second tag, which represents the years of the ones potentially interested into product.
I'm clueless on how to do this.
You should not store multiple values in a string column. It is wrong, wrong, wrong. SQL has a great way to store lists. It is called a table, which has rows and columns for each value.
That said, sometimes we are stuck with other people's really, really, really bad decisions. For those purposes, MySQL has a convenient function, find_in_set():
where find_in_set('a', tags) > 0
Your effort should go into fixing the data model, rather than trying to work around it.
Here is the query which make me to thought that how does OR operator in SQL works.
SELECT * FROM crop WHERE `crop_id`=1 OR ''='' ;
what i assumed that out put will be with records having crop_id equals to 1 because I have records with that value, and logically it does not evaluates the 2nd condition.
But it is returning all the records, rather than returning filtered(with crop_id) values.
Am I taking it wrong?
Update
I have a condition that if crop_id=1 matches it should return all the values having crop_id = 1 other wise return all records.
Upadte 2
Query mentioned above have both conditions are true, i.e crop_id=1 and ''='' I don't want alternatives, I just want to know that why it is ignoring first condition and taking 2nd condition.
OR
it works as in any boolean statement.
what i assumed that out put will be with records having crop_id equals to 1 because I have records with that value, and logically it does not evaluates the 2nd condition.
You are right, but if you don't have value equal to 1 then it evaluates the second part and it returns true and hence you always get result, either you match or not to your first part of OR.
The boolean logic of OR operator is as below:
true OR true = true
false OR true = true
true OR false = true
false OR false = false
So in your SQL query, you put '' = '' so your boolean logic evaluates to true for this, so you will get all results for this too. If you want to get results with crop_id = 1 only, then use below query:
SELECT * FROM crop WHERE `crop_id`=1
There is no need to use the OR.
For your original question the answer is:
WHERE something OR TRUE -- always TRUE
So you will get always all records.
For second question it looks like you want:
You pass in #param value specific value e.g. 1 or NULL;
SELECT *
FROM crop
WHERE crop_id = #param OR #param IS NULL;
or
SELECT *
FROM crop
WHERE crop_id = COALESCE(#param, crop_id);
You can also use IF ELSE for this like:
IF #param IS NULL THEN
SELECT *
FROM crop
ELSE
SELECT *
FROM crop
WHERE crop_id = #param
END IF
EDIT:
People says you don't need to know Math to be programmer.
This is exactly the case when Math(Boolean Logic) is needed.
╔═════════════╦═════════════╦═══════╗
║ Statement_1 ║ Statement_2 ║ AND ║
╠═════════════╬═════════════╬═══════╣
║ TRUE ║ TRUE ║ TRUE ║
║ FALSE ║ TRUE ║ FALSE ║
║ TRUE ║ FALSE ║ FALSE ║
║ FALSE ║ FALSE ║ FALSE ║
╚═════════════╩═════════════╩═══════╝
╔═════════════╦═════════════╦═══════╗
║ Statement_1 ║ Statement_2 ║ OR ║
╠═════════════╬═════════════╬═══════╣
║ TRUE ║ TRUE ║ TRUE ║
║ FALSE ║ TRUE ║ TRUE ║
║ TRUE ║ FALSE ║ TRUE ║
║ FALSE ║ FALSE ║ FALSE ║
╚═════════════╩═════════════╩═══════╝
Now Statement_2 is '' = '' which is always TRUE
╔═════════════╦═════════════╦══════╗
║ Statement_1 ║ Statement_2 ║ OR ║
╠═════════════╬═════════════╬══════╣
║ TRUE ║ TRUE ║ TRUE ║
║ FALSE ║ TRUE ║ TRUE ║
╚═════════════╩═════════════╩══════╝
As you see the result is always TRUE. And there is no need to evaluate Statement_1.
condition OR TRUE <=> TRUE (THIS IS TAUTOLOGY!!!)
Tautology is a formula that is true in every possible interpretation
And finally your condition is equivalent to:
WHERE 1=1 -- will return all records
for your desired output or will not be used rather if else will be used. i.e.
you have to
select count (*) from crop where 'crop_id'=1
if count>0
{
select * from crop where 'crop_id'=1
}else
{
select * from crop;
}
because or works like it will give all the results of equal to 1 and all the empty ones as well.
as or means either of the condition should be true.
so either it is 1 then it will come.
if it is empty than it will come
hence all will come.
so you have to use if else.
please do a google for the proper if else syntax. but logic will be these only.
thanks
The OR operator checks if any of the expressions are true, then it would return true.
In your case
''=''
is always true so the SELECT query returns all the records.
Below is the sample tables I'm working on
Table 1
╔════╦══════════════╦══════╗
║ KID║ REV ║ REDO ║
╠════╬══════════════╬══════╣
║ 1 ║ 43453453345 ║ 2 ║
║ 1 ║ 2433423423 ║ 1 ║
║ 2 ║ 23423423 ║ 1 ║
║ 2 ║ 5566533 ║ 2 ║
╚════╩══════════════╩══════╝
I need to insert REV, KID and REDO into table 1 and while inserting if there is a duplicate entry for both KID and REDO I need to just update REV.
I tried using ON DUPLICATE KEY UPDATE but I have two fields which are not primary here.
How to accomplish that with just plain mysql? Please help me
You could create a trigger which is triggered before an insert and checks if the KID and REDO id already exists. If they exist it executes an update.
The trigger could look for example like this:
delimiter //
CREATE TRIGGER upd_check BEFORE INSERT ON table1
FOR EACH ROW
BEGIN
SET rev = SELECT REV
FROM table1 as t
WHERE t.KID = NEW.KID
AND t.REDO = NEW.REDO;
IF rev != NEW.REV THEN
UPDATE table1
SET REV = NEW.REV
WHERE KID = NEW.KID
AND REDO = NEW.REDO;
END IF;
END;//
delimiter ;
But after the update the insert will still happen thats why you should create
a unique index for the REDO and KID. So if you insert a duplicate you get an error and the trigger will nevertheless update the REV.
CREATE UNIQUE INDEX unique_table1_idx on table1 (KID, REDO)
Another alternative is to send an signal and abort the insert, if the result of the SQL query is not null. Which means the insert will be create an duplicate entry.
signal sqlstate '45000' set message_text = 'duplicate insert';
I have a tables called post and look like below:
╔════════════════════════════════════╗
║ post_id origin_post_id rev ║
╠════════════════════════════════════╣
║ 1 1 1 ║
║ 2 1 2 ║
║ 3 3 1 ║
║ 4 3 2 ║
╚════════════════════════════════════╝
post_id is primary key and use auto_increment.
I use origin_post_id to store the origin post id.
I want to set origin_post_id as equal to post_id if it is a new post.
How to let me use
INSERT INTO POST
(POST_ID,
ORIGIN_POST_ID,
REV)
VALUES (NULL,
Default,
3)
What should I do when I create the table?
Is there some ways can get auto_increment key?
Run your above Query
INSERT INTO POST
(POST_ID,
ORIGIN_POST_ID,
REV)
VALUES (NULL,
Default,
3)
then Run update Query
UPDATE POST SET ORIGIN_POST_ID=POST_ID WHERE ORIGIN_POST_ID IS NULL