I have a question on MySQL paging. A user's record is displayed on a table with many other user's record. and the table is sorted/paged. Now I need to display the page that containing the user's row directly after the user login. How can I achieve this?
create table t_users (id int auto_increment primary key, username varchar(100)); insert t_users(username) values ('jim'),('bob'),('john'),('tim'),('tom'), ('mary'),('elise'),('karl'),('karla'),('bob'), ('jack'),('jacky'),('jon'),('tobias'),('peter');
I searched the google but not found answer so please help
There are two steps for this:
1. Determine the row's position in your sorted table.
Copied and tweaked from: https://stackoverflow.com/a/7057822/2391142
Use this SQL...
SELECT z.rank FROM (
SELECT id, #rownum := #rownum + 1 AS rank
FROM t_users, (SELECT #rownum := 0) r
ORDER BY id ASC
) as z WHERE id=1;
...replacing the ORDER BY id ASC with whatever your actual sort order is. And replacing the number 1 in WHERE id=1 with the provided number in that index.php?u=id url.
2. Determine the page number based on the row's position.
Use this PHP to determine the needed page number...
$rows_per_page = 50;
$user_row_position = [result you got from step 1];
$page = ceil($user_row_position / $rows_per_page);
...replacing the 50 with whatever your real rows-per-page limit is, and putting the real SQL result in $users_row_position.
And voila. You'll have the destination page number in the $page variable and hopefully you can take it from there.
EDIT
After further discussion in the comments, use this bit of PHP:
$page = 0;
$limit = 10;
// If a user ID is specified, then lookup the page number it's on.
if (isset($_GET['u'])) {
// Check the given ID is valid to avoid SQL injection risks.
if (is_numeric($_GET['u'])) {
// Lookup the user's position in the list.
$query = mysqli_fetch_array(mysqli_query($link, "SELECT z.rank FROM (SELECT id, #rownum := #rownum + 1 AS rank FROM sites, (SELECT #rownum := 0) r WHERE online='0') as z WHERE id=" . $_GET['u']));
$position = $query[0];
if (is_numeric($position)) {
// Convert the result to a number before doing math on it.
$position = (int) $position;
$page = ceil($position / $limit);
}
}
}
// If a page number is specified, and wasn't already set by looking a user, then lookup the real starting row.
if ($page == 0 && isset($_GET['page'])) {
// Check your given page number is valid too.
if (is_numeric($_GET['page'])) {
$page = (int) $_GET['page'];
}
}
// Notice that if anything fails in the above checks, we just pretend it never
// happened and keep using the default page and start number of 0.
// Determine the starting row based off the page number.
$start = ($page - 1) * $limit;
// Get the list of sites for the provided page only.
$query = mysqli_query($link, "SELECT * FROM sites WHERE online='0' LIMIT " . $start . ", " + $limit);
while ($row = mysqli_fetch_array($query)) {
// Stuff to render your rows goes here.
// You can use $row['fieldname'] to extract fields for this row.
}
Related
I need make search on billion records in MySQL and it's very long process (it's works now). May be Sphinx help me? How correctly to configure Sphinx for search numbers? Should I use integer attribute for searching (not string field)?
I need to get only row where the timestamp 'nearest or equal' to query:
CREATE TABLE test ( date TIMESTAMP(6) UNIQUE, num INT(32) );
| 2018-07-02 05:50:33.084011 | 282 |
| 2018-07-02 05:50:33.084028 | 475 |
...
(40 M such rows... all timestamps is unique, so this column are unique index so I no need in create additional index I suppose.)
sphinx.conf:
source src1
{
type = mysql
...
sql_query = SELECT * FROM test
}
indexer...
Sphinx 3.0.3
...
indexing index 'test'...
collected 40000000 docs, 0.0 MB
In my test I find nearest timestamp to query:
$start = microtime(true);
$query = '2018-07-02 05:50:33.084011';
$connMySQL = new PDO('mysql:host=localhost;dbname=test','','');
$sql = "SELECT * FROM test WHERE date <= '$search' ORDER BY date DESC LIMIT 1";
$que = $connMySQL->query($sql);
$result = $que->fetchAll(PDO::FETCH_ASSOC);
$query = $connMySQL->query('reset query cache');
$connMySQL = null;
print_r ($result);
echo 'Time MySQL:'.(microtime(true) - $start).' sec.';
$start = microtime(true);
$query = '2018-07-02 05:50:33.084029';
$connSphinxQL = new PDO('mysql:host=localhost;port=9306;dbname=test','root','');
$sql = "SELECT * FROM test WHERE date <= '$search' ORDER BY date DESC LIMIT 1";
$que = $connSphinxQL->query($sql);
$result = $que->fetchAll(PDO::FETCH_ASSOC);
$query = $connSphinxQL->query('reset query cache');
$connSphinxQL = null;
print_r ($result);
echo 'Time Sphinx:'.(microtime(true) - $start).' sec.';
Output:
[date] => 2018-07-02 05:50:33.084011 [num] => 282 Time MySQL: 0.00193 sec.
[date] => 2018-07-02 05:50:33.084028 [num] => 475 Time Sphinx: 0.00184 sec.
I suggested to see some different resuts, but noticed that before indexing I have got the same result, so I think Sphinx searches directy in MySQL by the reason of my wrong configuration.
Only ask here I found: no text search
Should I use integer attribute for searching (not string field)?
Yes. But an added complication, is a index NEEDS at least one field (sphinx isnt really designed as a general database, its intended for text queries!)
Can synthesize a fake one.
sql_query = SELECT unix_timestamp(`date`) AS id, 'a' AS field, num FROM test
sql_attr_uint = num
Also shows that need a unique integer as the first column, to be a document_id, seems as your timestamp is unique, can use that. a UNIX_TIMESTAMP is a nice easy way to represent a timestamp as a plain integer.
Can use id in queries too, for filtering, so would need to convert to a timestamp at the same time.
$query = '2018-07-02 05:50:33.084011';
$id = strtotime($query)
$sql = "SELECT * FROM test WHERE id <= '$id' ORDER BY id DESC LIMIT 1";
I want to get the 1st row of the result depends on which build the room is. For example Building 1 have 1-200 rooms and Building 2 have 201-400 rooms. The code I tried is below. I have used the MIN in the where clause but I got all the rooms instead of having one.
$query = $this->db->query("SELECT * FROM `ha_utility_reading`");
if ($query->num_rows == 0) {
echo "some data match";
$lastroom = $this->db->select("*")->from("rooms")
->where("(SELECT MIN(room_num) FROM ha_rooms) and bldg_num = '$bldg_num'")
->get()->result_array();
foreach($lastroom as $key => $test) {
$output['room_num'][] = $test['room_num'];
json_encode($output);
}
You get all the rows because you need a group by clause. Anyway, the best way to do this is just adding this to your query:
order by room_num asc limit 1;
Try this,
select * from rooms order by room_num asc limit 1;
I need to do a simple query, Select Statement
I want to search in Table all record with value "ValueA, ValueB".
If I use this code, not work well:
String255 valueToFilter;
valueToFilter = 'ValueA, ValueB';
select count (RecId) from MyTable
where MyTable.Field like valueToFilter ;
But not working, I need to keep all record with value "ValueA" or "ValueB", if in the file there is value like : "ValueA, ValueC" I want to get too.
I don't know the number of values (valueToFilter).
Thanks!
From my point of view the easiest way to accomplish this is to split your filter string:
String255 valueToFilterA = 'ValueA';
String255 valueToFilterB = 'ValueB';
;
select count (RecId) from MyTable
where MyTable.Field like valueToFilterA
|| MyTable.Field like valueToFilterB;
If you don't know the number of values you should use query object to add ranges dynamically:
Query query = new Query();
QueryRun queryRun;
QueryBuildDataSource qbds;
QueryBuildRange queryRange;
container conValues;
;
qbds = query.addDataSource(tableNum(MyTable));
for (i = 1; i <= conlen(conValues); i++)
{
queryRange = qbds.addRange(fieldNum(MyTable, Field));
queryRange.value(SysQuery::valueLike(conPeek(conValues, i)));
}
queryRun = new QueryRun(query);
info(strFmt("Records count %1", SysQuery::countTotal(queryRun)));
Below for each loop takes more time and i cant able to trace index usage using XREF as the table uses Oracle Schema.Please Help me.
Need to generate report for different Report Type ( Report type is the input parameter in my code ).Single report type may contain more than 50,000 records how to access all the record within minute.Index detail also mentioned below for each loop.
FOR EACH Report
FIELDS(EXTRACTDATE STATUS MailingType ReportType ReportNumber
RequestID CustID)
WHERE Report.EXTRACTDATE < Today
AND Report.ReportType = 'Customer Report'
AND Report.STATUS = 'Pending'
AND (Report.MailingType = "LETTER"
OR Report.MailingType = "Mail") NO-LOCK:
< Statements >
.
.
END.
**Index Detail**
CREATE INDEX "TYPE_IDX1" ON "Report" ("EXTRACTDATE" DESC, "ReportTYPE", "STATUS", "MailingTYPE", "PROGRESS_RECID")
CREATE INDEX "REQ_IDX1" ON "Report" ("REQUESTID", "PROGRESS_RECID")
CREATE INDEX "RTTYP_IDX1" ON "Report" ("ReportTYPE","PROGRESS_RECID")
The "OR" at the end will slow things down considerably - the AVM does better if you split it up into two sets of AND statements and OR the result, like so:
WHERE (Report.EXTRACTDATE < Today
AND Report.ReportType = 'Customer Report'
AND Report.STATUS = 'Pending'
AND Report.MailingType = "LETTER")
OR
(Report.EXTRACTDATE < Today
AND Report.ReportType = 'Customer Report'
AND Report.STATUS = 'Pending'
AND Report.MailingType = "Mail")
Here's a simple query we do for ad hoc requests from our Marketing department on the leads we received in the last 90 days.
SELECT ID
,FIRST_NAME
,LAST_NAME
,ADDRESS_1
,ADDRESS_2
,CITY
,STATE
,ZIP
,HOME_PHONE
,MOBILE_PHONE
,EMAIL_ADDRESS
,ROW_ADDED_DTM
FROM WEB_LEADS
WHERE ROW_ADDED_DTM BETWEEN #START AND #END
They are asking for more derived columns to be added that show the number of previous occurences of ADDRESS_1 where the EMAIL_ADDRESS matches. But they want is for different date ranges.
So the derived columns would look like this:
,COUNT_ADDRESS_1_LAST_1_DAYS,
,COUNT_ADDRESS_1_LAST_7_DAYS
,COUNT_ADDRESS_1_LAST_14_DAYS
etc.
I've manually filled these derived columns using update statements when there was just a few. The above query is really just a sample of a much larger query with many more columns. The actual request has blossomed into 6 date ranges for 13 columns. I'm asking if there's a better way then using 78 additional update statements.
I think you will have a hard time writing a query that includes all of these 78 metrics per e-mail address without actually creating a query that hard-codes the different choices. However you can generate such a pivot query with dynamic SQL, which will save you some keystrokes and will adjust dynamically as you add more columns to the table.
The result you want to end up with will look something like this (but of course you won't want to type it):
;WITH y AS
(
SELECT
EMAIL_ADDRESS,
/* aggregation portion */
[ADDRESS_1] = COUNT(DISTINCT [ADDRESS_1]),
[ADDRESS_2] = COUNT(DISTINCT [ADDRESS_2]),
... other columns
/* end agg portion */
FROM dbo.WEB_LEADS AS wl
WHERE ROW_ADDED_DTM >= /* one of 6 past dates */
GROUP BY wl.EMAIL_ADDRESS
)
SELECT EMAIL_ADDRESS,
/* pivot portion */
COUNT_ADDRESS_1_LAST_1_DAYS = *count address 1 from 1 day ago*,
COUNT_ADDRESS_1_LAST_7_DAYS = *count address 1 from 7 days ago*,
... other date ranges ...
COUNT_ADDRESS_2_LAST_1_DAYS = *count address 2 from 1 day ago*,
COUNT_ADDRESS_2_LAST_7_DAYS = *count address 2 from 7 days ago*,
... other date ranges ...
... repeat for 11 more columns ...
/* end pivot portion */
FROM y
GROUP BY EMAIL_ADDRESS
ORDER BY EMAIL_ADDRESS;
This is a little involved, and it should all be run as one script, but I'm going to break it up into chunks to intersperse comments on how the above portions are populated without typing them. (And before long #Bluefeet will probably come along with a much better PIVOT alternative.) I'll enclose my interspersed comments in /* */ so that you can still copy the bulk of this answer into Management Studio and run it with the comments intact.
Code/comments to copy follows:
/*
First, let's build a table of dates that can be used both to derive labels for pivoting and to assist with aggregation. I've added the three ranges you've mentioned and guessed at a fourth, but hopefully it is clear how to add more:
*/
DECLARE #d DATE = SYSDATETIME();
CREATE TABLE #L(label NVARCHAR(15), d DATE);
INSERT #L(label, d) VALUES
(N'LAST_1_DAYS', DATEADD(DAY, -1, #d)),
(N'LAST_7_DAYS', DATEADD(DAY, -8, #d)),
(N'LAST_14_DAYS', DATEADD(DAY, -15, #d)),
(N'LAST_MONTH', DATEADD(MONTH, -1, #d));
/*
Next, let's build the portions of the query that are repeated per column name. First, the aggregation portion is just in the format col = COUNT(DISTINCT col). We're going to go to the catalog views to dynamically derive the list of column names (except ID, EMAIL_ADDRESS and ROW_ADDED_DTM) and stuff them into a #temp table for re-use.
*/
SELECT name INTO #N FROM sys.columns
WHERE [object_id] = OBJECT_ID(N'dbo.WEB_LEADS')
AND name NOT IN (N'ID', N'EMAIL_ADDRESS', N'ROW_ADDED_DTM');
DECLARE #agg NVARCHAR(MAX) = N'', #piv NVARCHAR(MAX) = N'';
SELECT #agg += ',
' + QUOTENAME(name) + ' = COUNT(DISTINCT '
+ QUOTENAME(name) + ')' FROM #N;
PRINT #agg;
/*
Next we'll build the "pivot" portion (even though I am angling for the poor man's pivot - a bunch of CASE expressions). For each column name we need a conditional against each range, so we can accomplish this by cross joining the list of column names against our labels table. (And we'll use this exact technique again in the query later to make the /* one of past 6 dates */ portion work.
*/
SELECT #piv += ',
COUNT_' + n.name + '_' + l.label
+ ' = MAX(CASE WHEN label = N''' + l.label
+ ''' THEN ' + QUOTENAME(n.name) + ' END)'
FROM #N as n CROSS JOIN #L AS l;
PRINT #piv;
/*
Now, with those two portions populated as we'd like them, we can build a dynamic SQL statement that fills out the rest:
*/
DECLARE #sql NVARCHAR(MAX) = N';WITH y AS
(
SELECT
EMAIL_ADDRESS, l.label' + #agg + '
FROM dbo.WEB_LEADS AS wl
CROSS JOIN #L AS l
WHERE wl.ROW_ADDED_DTM >= l.d
GROUP BY wl.EMAIL_ADDRESS, l.label
)
SELECT EMAIL_ADDRESS' + #piv + '
FROM y
GROUP BY EMAIL_ADDRESS
ORDER BY EMAIL_ADDRESS;';
PRINT #sql;
EXEC sp_executesql #sql;
GO
DROP TABLE #N, #L;
/*
Now again, this is a pretty complex piece of code, and perhaps it can be made easier with PIVOT. But I think even #Bluefeet will write a version of PIVOT that uses dynamic SQL because there is just way too much to hard-code here IMHO.
*/