Trouble combining Two sql queries into one - php

I have a table which contains due dates for individual member records. Each row contains four fields:
ID | Next_Due | Next_Due_Missed | Amount
=============================================================
123 | 2010-12-05 | NULL | 41.32
456 | 2010-12-10 | 2010-12-05 | 21.44
789 | 2010-12-20 | 2010-12-10 | 39.99
ID is the unique id of each MEMBER
Next Due - is the next due day of their regular subscription period
Next_Due_Missed is populated ONLY if there was an error collecting the first round of subscription payment.
Amount is amount owned for subscription.
My goal is to create a sql query that checks if next_due_missed exists and is not null. If it does, use that value as the '$date'. If not, set $date = value of next_due
this is done easily enough except my results are grouped by Next_Due in normal circumstances and will omit next_due_missed if I combine the way I currently am.
Every payment period, there may be 600+ records with next_due equal to the desired date (and 10-15 equal to next_due_missed).
My current query is:
$stmt = $db->prepare("SELECT next_due, next_due_missed FROM table_name WHERE (next_due > CURDATE() OR next_due_missed > CURDATE()) GROUP BY next_due ASC");
This only returns results for next_due however. Omitting the GROUP BY clause returns hundreds of results (while I need to group in this stage).
Similarly at a later point, I will need to break out those individual records and actually create payment records based on the 'next_due' and 'next_due_missed' values.
Any ideas what I am missing?

I am not sure the purpose of your GROUP BY other than to get DISTINCT values, but left it in in case you provided a partial query:
SELECT coalesce(next_due_missed, next_due) as EffectiveNextDue
FROM table_name
WHERE coalesce(next_due_missed, next_due) > CURDATE()
GROUP BY coalesce(next_due_missed, next_due)

Related

Limit number of MySQL results per user

I have a site where users can list items for sale. The front of the site displays random items on sale using the following SQL.
SELECT * FROM auctions
WHERE closed = 0 AND suspended = 0 AND starts <= 1390990443
ORDER BY RAND() LIMIT 30
The problem is that one users has a lot of sales to the extent that the front of the site is mostly that one user. I would like to limit the items displayed by the user using the auction.user column to five items per user.
I am hoping that there is SQL to do this in one line, if not then advice or a link on who to build up results on multiple queries would be appreciated. Coded language is PHP.
Edit: database structure is here. http://pastebin.com/3ua18k4h
Personally I would try and form some solution pulling results into PHP and filtering them there, but that's because I feel more comfortable in PHP than MySQL. However you could do something like this without the PHP filtering and just via your MySQL query, no idea how it would do scaling up to lots of users though (not sure on your full table schema, so I improvised)
SELECT ID, USERNAME, AUCTION_ID
FROM
(
SELECT *, #row:=IF(username=#username,#row,0)+1 AS auctioncount, #username:=username FROM
(SELECT *,RAND() AS trand FROM table1) t1,
(SELECT #row:=0,#username:='') tm2
ORDER BY username,trand
) t2
WHERE auctioncount<=5 LIMIT 30
SQL fiddle: http://sqlfiddle.com/#!2/9bd47/1
You can change auctioncount to the maximum number of listings you want per user.
You can get more results with MYSQL with a bigger LIMIT and then create an array for every items for sale by user and finally take only 5 items per user, so you will only use 1 request to Mysql and more CPU usage, could be a good idea if you have a lot of traffic on your web site.
1) get more results :
SELECT * FROM auctions
WHERE closed = 0 AND suspended = 0 AND starts <= 1390990443
ORDER BY RAND() LIMIT 1000
2) Loop and store per items per user :
foreach($results as $item) {
array_push($itemsPerUser[$item['userId']], $item);
}
3) Filter only 5 items per user:
foreach($itemsPerUser as $user => $items) {
$fiveItemsPerUser = array_slice($items, 0, 5);
}
P.S: this is pseudo-code, you should add more check on array length etc ...
Not quite a direct answer to your problem, but perhaps following is enough:
Select 30 random users with running auctions, and display the oldest auction of each of them:
SELECT * FROM auctions WHERE id IN (
SELECT MIN(id) FROM auctions
WHERE closed = 0 AND suspended = 0 AND starts <= 1390990443
GROUP BY userid ORDER BY RAND() LIMIT 30
)
At least you can remove the newlines and have it all in a single line sql query, which can't be done with the exact query you requested.
Here's something to think about, although I appreciate that the intellectual leap from this to a (fair and) working solution might be a step too far!...
SELECT RAND(#i:=RAND()*1000);
+-----------------------+
| RAND(#i:=RAND()*1000) |
+-----------------------+
| 0.7903550134881911 |
+-----------------------+
SELECT RAND(#i);
+--------------------+
| RAND(#i) |
+--------------------+
| 0.7903550134881911 |
+--------------------+
SELECT RAND(#i:=RAND()*1000);
+-----------------------+
| RAND(#i:=RAND()*1000) |
+-----------------------+
| 0.9555754568014065 |
+-----------------------+

Select random row per distinct field value?

I have a MySQL query that results in something like this:
person | some_info
==================
bob | pphsmbf24
bob | rz72nixdy
bob | rbqqarywk
john | kif9adxxn
john | 77tp431p4
john | hx4t0e76j
john | 4yiomqv4i
alex | n25pz8z83
alex | orq9w7c24
alex | beuz1p133
etc...
(This is just a simplified example. In reality there are about 5000 rows in my results).
What I need to do is go through each person in the list (bob, john, alex, etc...) and pull out a row from their set of results. The row I pull out is sort of random but sort of also based on a loose set of conditions. It's not really important to specify the conditions here so I'll just say it's a random row for the example.
Anyways, using PHP, this solution is pretty simple. I make my query and get 5000 rows back and iterate through them pulling out my random row for each person. Easy.
However, I'm wondering if it's possible to get what I would from only a MySQL query so that I don't have to use PHP to iterate through the results and pull out my random rows.
I have a feeling it might involve a BUNCH of subselects, like one for each person, in which case that solution would be more time, resource and bandwidth intensive than my current solution.
Is there a clever query that can accomplish this all in one command?
Here is an SQLFiddle that you can play with.
To get a random value for a distinct name use
SELECT r.name,
(SELECT r1.some_info FROM test AS r1 WHERE r.name=r1.name ORDER BY rand() LIMIT 1) AS 'some_info'
FROM test AS r
GROUP BY r.name ;
Put this query as it stands in your sqlfiddle and it will work
Im using r and r1 as table alias names. This will also use a subquery to select a random some_info for the name
SQL Fiddle is here
My first response would be to use php to generate a random number:
$randId = rand($min, $max);
Then run a SQL query that only gets the record where your index equals $randID.
Here is the solution:
select person, acting from personel where id in (
select lim from
(select count(person) c, min(id) i, cast(rand()*(count(person)-1) +min(id)
as unsigned) lim from personel group by person order by i) t1
)
The table used in the example is below:
create table personel (
id int(11) not null auto_increment,
person char(16),
acting char(19),
primary key(id)
);
insert into personel (person,acting) values
('john','abd'),('john','aabd'),('john','adbd'),('john','abfd'),
('alex','ab2d'),('alex','abd3'),('alex','ab4d'),('alex','a6bd'),
('max','ab2d'),('max','abd3'),('max','ab4d'),('max','a6bd'),
('jimmy','ab2d'),('jimmy','abd3'),('jimmy','ab4d'),('jimmy','a6bd');
You can limit the number of queries, and order by "rand()" to get your desired result.
Perhaps if you tried something like this:
SELECT name, some_info
FROM test
WHERE name = 'tara'
ORDER BY rand()
LIMIT 1

Detecting spammers with MySQL

I see an ever increasing number of users signing up on my site to just send duplicate SPAM messages to other users. I've added some server side code to detect duplicate messages with the following mysql query:
SELECT count(content) as msgs_sent
FROM messages
WHERE sender_id = '.$sender_id.'
GROUP BY content having count(content) > 10
The query works well but now they're getting around this by changing a few charctersr in their messages. Is there a way to detect this with MySQL or do I need to look at each grouping returned from MySQL and then use PHP to determine the percentage of similarity?
Any thoughts or suggestions?
Fulltext Match
You could look at implementing something similar to the MATCH example here:
mysql> SELECT id, body, MATCH (title,body) AGAINST
-> ('Security implications of running MySQL as root') AS score
-> FROM articles WHERE MATCH (title,body) AGAINST
-> ('Security implications of running MySQL as root');
+----+-------------------------------------+-----------------+
| id | body | score |
+----+-------------------------------------+-----------------+
| 4 | 1. Never run mysqld as root. 2. ... | 1.5219271183014 |
| 6 | When configured properly, MySQL ... | 1.3114095926285 |
+----+-------------------------------------+-----------------+
2 rows in set (0.00 sec)
So for your example, perhaps:
SELECT id, MATCH (content) AGAINST ('your string') AS score
FROM messages
WHERE MATCH (content) AGAINST ('your string')
AND score > 1;
Note that to use these functions your content column would need to be a FULLTEXT index.
What is score in this example?
It is a relevance value. It is computed through the process described below:
Every correct word in the collection and in the query is weighted
according to its significance in the collection or query.
Consequently, a word that is present in many documents has a lower
weight (and may even have a zero weight), because it has lower
semantic value in this particular collection. Conversely, if the word
is rare, it receives a higher weight. The weights of the words are
combined to compute the relevance of the row.
From the documentation page.

PHP/MySQL Reservation System

I'm currently working on an Equipment Reservation System for my school.
Here's basically what my tables look like:
tblEquipment:
id name description
1 Camera Takes pictures
2 Projector Projects images
3 Stereo Plays music
tblEvents:
id equipmentID start end
1 2,3 1251312300 1251315900 //Should I use comma delimited entries for equipmentID?
2 1 1251312300 1251315900
Regarding my project, I have a couple of questions:
1) If multiple pieces of equipment are being reserved, (which will happen more times than not) should the "equipmentIDs" be comma delimited in the equipmentID field?
2) Currently, when a user makes a reservation, they first select their "requested times", then are presented with available items at that time. Here's what I am using for that query:
$start = //user's requested time
$start = //user's requested time
SELECT equipmentID FROM tblEvents
WHERE ($start >= start && $start <= end)
OR ($end >= start && $end <= end)
OR ($start <= start && $end >= end
while($row = mysql_fetch_array($data)) {
echo $row['equipmentID']; //Would echo something like:
echo "<br>"; // 2,3
// 1
}
My question is this:
How can I take the 'results' of the above query to then re-query the 'tblequipment' table, but exclude the items that were in the 'results' above (because they would not be available). Keeping in mind, that my query above may return multiple rows.
Any help on this would be great, thanks!
Regarding #1: No! No, no, no, no, no! If you have multiple pieces of equipment being reserved, then you should have multiple rows in the reservations table (what looks like tblEvents here). To avoid duplicating the other fields in tblEvents, you'd typically create a third table, perhaps tblEventEquipment that simply lists what equipment belongs with what event.
If you need a comma-separated list for the purposes of output (which doesn't seem likely), you can always generate one with GROUP_CONCAT(), but inside the table you want one row per reserved piece of equipment. Otherwise, SQL cannot effectively (or efficiently) determine what equipment is reserved at a particular time.
Regarding #2, you want a query like:
SELECT *
FROM tblEquipment
WHERE NOT EXISTS (
SELECT 1
FROM tblEvents
WHERE tblEvents.equipmentID = tblEquipment.equipmentID
AND $end >= start AND $start <= end
)
This selects equipment for which there isn't a reservation. Note that I simplified your logic for determining whether the equipment is reserved or not by doing just two comparisons.
Finally, an unrelated note: I recommend strongly against storing timestamps as integers in the database table. Use MySQL's built-in DATETIME type. If you need to convert to/from a timestamp, you can use the UNIX_TIMESTAMP() and FROM_UNIXTIME() functions.
No, don't use comma-seperated values. If you want a user to have the ability to check-out multiple items, you'll need a new table:
Tables: Users, Property, Checkout
The new Checkout table will have the following fields:
id
person_id
property_id
checkout_date
checkin_date
This table can have multiple entries for any particular user. A user may be in there once for a company laptop, and again for a company projector:
1 | 12 | 23 | 2009-08-17 | 0000-00-00
2 | 12 | 28 | 2009-08-17 | 0000-00-00
As for checking if an item is reserved, I would as a field in the property table to hold a boolean value:
is_reserved (BOOL)
Finding items that are available is nothing more than checking all items with a BOOL value of false, and no presence in the checkout table coupled with no checkin_date.

mysql - search between dates where all dates appear

I'm working with some imported data that stores details about whether a "room" is available on a specific day or not. Each room has an individual entry for the date that it is available.
| id | date | price |
--------------------------------
| 1 | 2010-08-04 | 45.00 |
A user can search across a date range and the search needs to bring back the relevant rooms that are available between those two dates.
In other words using a sql query to search:
where date>=2010-08-04 AND date<=2010-08-09
would not suffice as this would bring back all rooms available at SOME point between the chosen dates not the rooms that are available for ALL of the dates concerned.
I am considering using a temporary date table in some way to cross-reference that there is an entry for every date in the range but are uncertain as to the best way to implement this.
The end code platform is PHP and I'm also exploring whether the data can be processed subsequently within the code but would like to keep everything with the sql if possible.
Any suggestions that put forward would be greatly appreciated.
Thanks
Update: my original answer was identical to Quassnoi's but 1 minute too late, so I decided to delete it and do something different instead. This query does not assume that (id, date) is unique. If there is more than one entry, it selects the cheapest. Also, it also sums the total cost and returns that too which might also be useful.
SELECT id, SUM(price) FROM (
SELECT id, date, MIN(price) AS price
FROM Table1
GROUP BY id, date) AS T1
WHERE `date` BETWEEN '2010-08-05' AND '2010-08-07'
GROUP BY id
HAVING COUNT(*) = DATEDIFF('2010-08-07','2010-08-05') + 1
Provided that (id, date) combination is unique:
SELECT id
FROM mytable
WHERE date BETWEEN '2010-08-04' AND '2010-08-09'
GROUP BY
id
HAVING COUNT(*) = DATEDIFF('2010-08-09', '2010-08-04') + 1
Make sure you have a UNIQUE constraint on (id, date) and the date is stored as DATE, not DATETIME.

Categories