I have a PHP script displaying a list of players sorted by their "virtual money":
$sth = $db->prepare("
select u.id,
u.first_name,
u.city,
u.avatar,
m.money,
u.login > u.logout as online
from pref_users u, pref_money m where
m.yw=to_char(current_timestamp, 'IYYY-IW') and
u.id=m.id
order by m.money desc
limit 20 offset ?
");
$sth->execute(array($_GET['offset']));
To show a player position in the list I use a PHP variable $pos which is incremented in a loop while printing their names and further data.
I would like to have that position in the SQL statement instead of PHP for various reasons. So I'm trying the following:
$sth = $db->prepare("
select u.id,
row_number() + ? as pos,
u.first_name,
u.city,
u.avatar,
m.money,
u.login > u.logout as online
from pref_users u, pref_money m where
m.yw=to_char(current_timestamp, 'IYYY-IW') and
u.id=m.id
order by m.money desc
limit 20 offset ?
");
$sth->execute(array($_GET['offset'], $_GET['offset']));
But get the ERROR: window function call requires an OVER clause
I'm trying to add over(m.money) but get a syntax error.
I'm probably misunderstanding the Window Functions doc.
Check the user notes on: http://www.postgresql.org/docs/8.4/interactive/functions-window.html
You will need the Over() to contain the same order by clause as the whole query:
$sth = $db->prepare("
select u.id,
row_number() OVER (order by m.money desc) + ? as pos,
u.first_name,
u.city,
u.avatar,
m.money,
u.login > u.logout as online
from pref_users u, pref_money m where
m.yw=to_char(current_timestamp, 'IYYY-IW') and
u.id=m.id
order by m.money desc
limit 20 offset ?
");
$sth->execute(array($_GET['offset'], $_GET['offset']));
You want row_number() OVER (ORDER BY m.money) + ? etc.
Related
I am trying to run a query to find the count and percentage of artisan based on gender. The query is running absolutely fine on the SQL server 8. But not on my live server which is 4.9.5.
The below is my query.
SELECT industry
, count(*) as cnt
, (count(*)*100.0/sum(count(*))over()) as perc
FROM `artisan_work`
GROUP BY industry
ORDER BY cnt DESC
In any database, you should be able to use:
SELECT aw.industry, count(*) as cnt,
count(*) * 100.0 / tot.cnt as perc
FROM artisan_work aw cross join
(SELECT COUNT(*) as cnt FROM artisan_work) tot
GROUP BY aw.industry
ORDER BY cnt DESC
SELECT industry
, count(*) as cnt
, (count(*)*100.0/(select count(*) from artisan_work )) as perc
FROM artisan_work a
GROUP BY industry
ORDER BY cnt DESC
I'm using php pdo with SQL Server Native Client 11.0 as the driver.
Obiously I do cannot use LIMIT clause as it is proprietary and in trying to workarounf selecting ranges of rows I keep hitting issues.
My setup as follows :
$statement = "SELECT ROW_NUMBER() OVER (ORDER BY USERS.name DESC) AS RowNum, CASE.no, CASE.type, CASE.date, CASE.ju, USERS.name, TBRLMF.rd_rl_description FROM dbo.CASE INNER JOIN dbo.USERS ON dbo.CASE.no = dbo.USERS.case_no INNER JOIN dbo.CMSTBRLMF ON dbo.USERS.relationship = dbo.TBRLMF.code ";
if($exact == 'checked'){
$exact = '=';
}else{
$exact = 'LIKE';
}
if($searchtype == 'users'){
$statement .= " WHERE USERS.name $exact '%$searchstring%'";
}else{
$statement .= " WHERE USERS.no $exac '%$searchstring%'";
}
$statement .= " AND RowNum BETWEEN :offset AND :max";
$statement = $dbh->prepare($statement);
$statement->bindParam(':offset', $offset, PDO::PARAM_INT);
$statement->bindParam(':max', $max, PDO::PARAM_INT);
and as long as I do not include the line of code :
$statement .= " AND RowNum BETWEEN :offset AND :max";
It works fine but gives me all the data.
When that line is included I recieve the following error :-
caught exception 'PDOException' with message 'SQLSTATE[42S22]: Column not found: 207 [Microsoft][SQL Server Native Client 11.0][SQL Server]Invalid column name 'RowNum'. (SQLExecute[207] at ext\pdo_odbc\odbc_stmt.c:254)' in C:\pub\test\Classes\Core.php:44
I am at a loss and MSSQL is new to me.
Help much appreciated.
The predicate on that column can't be in a WHERE clause. (The predicates in the WHERE clause are evaluated when the rows are accessed; the value of that expression (analytic function) isn't available until after the rows are accessed.
You may be able to reference the column alias in a HAVING clause:
" HAVING RowNum BETWEEN ... ";
You can't use COLUMN ALIAS (RowNum in your case) in WHERE clause. Rather change your query using derived table like below
SELECT * FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY USERS.name DESC) AS RowNum,
CASE.no,
CASE.type,
CASE.date,
CASE.ju,
USERS.name,
TBRLMF.rd_rl_description
FROM dbo.CASE
INNER JOIN dbo.USERS
ON dbo.CASE.no = dbo.USERS.case_no
INNER JOIN dbo.CMSTBRLMF
ON dbo.USERS.relationship = dbo.TBRLMF.code
WHERE USERS.name LIKE '%string%'
AND USERS.no LIKE '%string%'
) X
WHERE RowNum BETWEEN 1 AND 10;
EDIT:
You can also use CTE (Common Table Expression) to get around this like below
Create the CTE
WITH NEWCTE AS
(
SELECT ROW_NUMBER() OVER (ORDER BY USERS.name DESC) AS RowNum,
CASE.no,
CASE.type,
CASE.date,
CASE.ju,
USERS.name,
TBRLMF.rd_rl_description
FROM dbo.CASE
INNER JOIN dbo.USERS
ON dbo.CASE.no = dbo.USERS.case_no
INNER JOIN dbo.CMSTBRLMF
ON dbo.USERS.relationship = dbo.TBRLMF.code
WHERE USERS.name LIKE '%searchstring%'
AND USERS.no LIKE '%searchstring%'
)
Query the CTE
SELECT *
FROM NEWCTE
WHERE RowNum BETWEEN 1 AND 10
I am trying to use two JOIN statements in one query,
$sqlsorgu = mysql_query("SELECT *, COUNT(*), AVG(clicks), AVG(scrolls), AVG(spent)
FROM track where referid='".$memberr."' GROUP BY referer ORDER BY id desc limit 15
JOIN
(
select id, country, num, num*100/total pct
from (SELECT id,country, count(*) as num
FROM track GROUP BY country
ORDER BY num desc limit 5) x
join (select count(*) total from track) y
) tc on t.id = tc.id") or die(mysql_error());
but I am getting this error:
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'JOIN ( select id, country, num, num*100/total pct from (SELECT id,country' at line 1
What is the correct way to use it ?
GROUP BY/ WHERE/ Order by come after join statements. Try reording like:
"SELECT *, COUNT(*), AVG(clicks), AVG(scrolls), AVG(spent)
FROM track t
JOIN
(
select id, country, num, num*100/total pct
from (SELECT id,country, count(*) as num
FROM track GROUP BY country
ORDER BY num desc limit 5) x
join (select count(*) total from track) y
) tc on t.id = tc.id
where referid='".$memberr."'
GROUP BY referer
ORDER BY tc.id desc limit 15
I have a query to pull all articles out of the database..
$get_articles = $db->query("
SELECT *, a.id AS id, s.type AS type FROM ".$prefix."articles a
LEFT JOIN ".$prefix."sources s ON (s.id = source_id)
WHERE a.type!='trashed' ORDER BY a.timestamp DESC LIMIT $start, $end");
Within the loop of this query, I do then do another query on the same table to find related articles to the 'title' of the article, stored as '$related_phrase'. The query within the loop is:
// get related articles to this entry
$get_related = $db->query("
SELECT *, a.id AS id, MATCH (title, description) AGAINST ('$related_phrase') AS score FROM ".$prefix."articles a
LEFT JOIN ".$prefix."sources s ON (s.id = source_id) WHERE a.type!='trashed' AND MATCH (title, description) AGAINST ('$related_phrase') AND a.id!='$articles[id]' HAVING score > 7
ORDER BY a.timestamp DESC LIMIT 0, 3");
This basically means we have a query in a loop which is causing the pages to load very slowly.
What we want to do, is bring the query from within the loop, in the main query, so it's all working within one query, if that's possible?
Any help very much appreciated!
I don't think you would gain much speed by merging the two queries.
One thing you could try is to get a list of all articles and DISTINCT searchphrases (in e.g. temptable), and then build a query to get all related articles in one single go. Lastly match up related articles with the article list.
try this:
$articles_and_related = $db->query("
SELECT *
FROM ".$prefix."articles art
LEFT JOIN (
SELECT * FROM ".$prefix."articles x
WHERE
score > 7
AND x.type != 'trashed'
AND x.id != art.id
AND MATCH(x.title, x.description) AGAINST (art.title)
LIMIT 3
) rel
LEFT JOIN ".$prefix."sources s2 ON (s2.id = rel.source_id)
LEFT JOIN ".$prefix."sources s ON (s.id = art.source_id)
WHERE
art.type!='trashed'
ORDER BY art.timestamp DESC LIMIT $start, $end");
I have created a feature where users can start new topics (similar to forums).
At the moment on a page, the query for the topics are as follows:
$q = "SELECT ".TBL_COMMUNITYTHREADS.".title, ".TBL_COMMUNITYTHREADS.".id,
".TBL_COMMUNITYTHREADS.".date, ".TBL_COMMUNITYTHREADS.".author, ".TBL_USERS.".username FROM ".TBL_COMMUNITYTHREADS."
INNER JOIN ".TBL_USERS." ON ".TBL_COMMUNITYTHREADS.".author = ".TBL_USERS.".id
WHERE type = '$type'
ORDER BY date DESC LIMIT $offset, $rowsperpage ";
The tables are constants and the offset and rowsperpage are variables passed in to limit how many posts are on a page.
At the moment though, all the topics are ordered by the date.
I want them to be ordered by the latest response.
Similarly to forums, when the reponse inside the topic is newest, that topic will go to the top.
The topics are stored in tbl_communitythreads and the replies in tbl_communityreplies.
How can I ordered them by the latest repsonse.
They are linked by the threadid in tbl_communityreplies. Also in that one is the date column.
Thankyou for reading, I just cant think of how to do this.
This:
SELECT c.title, c.id, c.date, c.author, u.username,
(
SELECT MAX(reply_date)
FROM tbl_communityreplies cr
WHERE cr.thread = c.id
) AS last_reply
FROM TBL_COMMUNITYTHREADS c
JOIN TBL_USERS u
ON u.id = c.author
ORDER BY
last_reply DESC, c.id DESC
LIMIT $offset, $rowsperpage
or this:
SELECT c.title, c.id, c.date, c.author, u.username
FROM (
SELECT cr.thread, cr.reply_date, cr.id
FROM tbl_communityreplies cr
WHERE (cr.last_reply, cr.id) =
(
SELECT last_reply, id
FROM tbl_communityreplies cri
WHERE cri.thread = cr.thread
ORDER BY
thread DESC, last_reply DESC, id DESC
)
ORDER BY
last_reply DESC, id DESC
LIMIT $offset, $rowsperpage
) q
JOIN TBL_COMMUNITYTHREADS c
ON c.id = q.thread
JOIN TBL_USERS u
ON u.id = c.author
ORDER BY
q.reply_date DESC, q.id DESC
The former query is more efficient for large values of $offset, or if you have few threads with lots of replies.
Issue the following commands:
CREATE INDEX ix_communitythreads_replydate_id ON TBL_COMMUNITYTHREADS (reply_date, id)
CREATE INDEX ix_communitythreads_thread_replydate_id ON TBL_COMMUNITYTHREADS (thread, reply_date, id)
for this to work fast.
by join with another table , then you can order by column from this table.