So I have a loop to be nested inside another loop based on two queries. I have the first loop working fine-
$sql_categories = mysql_query("SELECT * FROM $categories_table");
$results = mysql_query("SELECT * FROM $events_table");
while ($num_rows = mysql_fetch_assoc($sql_categories)) {
extract($num_rows);
echo "<h2>$category_name</h2>";
// Begin second loop to output events
while/for(not sure) {
}
}
I want to output into the second loop all the $vars for the corresponding $category_id. In the second query, the matching value is $event_category_id.
I don't know if that makes sense, but what I'm trying to get is basically--
<h2>Category One</h2>
Event Name
Event Name
Event Name
<h2>Category Two</h2>
Event Name
Event Name
Event Name
etc. where the "Event Name" corresponds to the "Category Name"
The two tables I'm working with look like this-
CREATE TABLE `wp_wild_dbem_categories` (
`category_id` int(11) NOT NULL auto_increment,
`category_name` tinytext NOT NULL,
PRIMARY KEY (`category_id`)
) ENGINE=MyISAM AUTO_INCREMENT=5 DEFAULT CHARSET=latin1
CREATE TABLE `wp_wild_dbem_events` (
`event_id` mediumint(9) NOT NULL auto_increment,
`event_author` mediumint(9) default NULL,
`event_name` tinytext NOT NULL,
`event_start_time` time NOT NULL default '00:00:00',
`event_end_time` time NOT NULL default '00:00:00',
`event_start_date` date NOT NULL default '0000-00-00',
`event_end_date` date default NULL,
`event_notes` text,
`event_rsvp` tinyint(1) NOT NULL default '0',
`event_seats` tinyint(4) default NULL,
`event_contactperson_id` mediumint(9) default NULL,
`location_id` mediumint(9) NOT NULL default '0',
`recurrence_id` mediumint(9) default NULL,
`event_category_id` int(11) default NULL,
UNIQUE KEY `event_id` (`event_id`)
) ENGINE=MyISAM AUTO_INCREMENT=26 DEFAULT CHARSET=latin1
Thanks for your help!
You need to do the second query inside the while loop for it to have any meaningful effect:
$sql_categories = mysql_query("SELECT * FROM $categories_table");
while($category = mysql_fetch_assoc($sql_categories)) {
extract($category);
$events = mysql_query("SELECT * FROM $events_table WHERE event_category_id = '".mysql_real_escape_string($category_id)."'");
echo "<h2>$category_name</h2>";
while($event = mysql_fetch_assoc($events) {
extract($category);
echo "<p>$event_name</p>";
}
}
This should get you where you want, but note that this is not the optimal way to do things. You should first get all of the events, build an array of them indexed by event_category_id and loop that array inside your while loop. This is because now you are doing one extra query per each category whereas only two queries in total should suffice.
But perhaps this would get you started on that then.
Related
My business has a simple invoicing system written by me a few years ago in php with a MySQL database. When I wrote it, I knew just enough to be dangerous. While it does work, a lot of the code is very inefficient, slow and poorly conceived, so I'm re-writing some of it in the hopes of fixing it.
There are two tables: customers and transactions. When invoices need to be created, there are two queries, the second unfortunately in a while loop. Here is some simplified pseudo-code to show what I'm doing:
// Get list of customers who owe something
SELECT name, address
FROM customers
WHERE
(SELECT COUNT(*) FROM transactions
WHERE transactions.customer_id = customers.id
AND owed > 0)
> 0
ORDER BY address
//loop through that result and query the transactions table to get a
//list of charges for each customer. Like:
while ($row = customer_array()) {
echo name_and_address;
SELECT * FROM transactions WHERE id = $row['customer_id']
AND owed = TRUE ORDER BY date
while ($row = transactions_array()) {
echo each_transaction_row;
}
So obviously the nested loops, subqueries and queries in loops are bad, worse and slow. I've tried joins but can't seem to figure out how to make them work correctly in this context. What is the correct way to do something like this? Can it be done with a single query?
Table structure:
CREATE TABLE `customer_data` (
`account_id` int(6) NOT NULL AUTO_INCREMENT,
`service_address` varchar(255) DEFAULT NULL,
`service_zipcode` int(5) DEFAULT NULL,
`first_name` varchar(255) DEFAULT NULL,
`last_name` varchar(255) DEFAULT NULL,
`billing_address1` varchar(255) DEFAULT NULL,
`billing_address2` varchar(255) DEFAULT NULL,
`billing_city` varchar(255) DEFAULT NULL,
`billing_state` varchar(2) DEFAULT NULL,
`billing_zipcode` varchar(20) DEFAULT NULL,
`phone1` varchar(100) DEFAULT NULL,
`phone2` varchar(100) DEFAULT NULL,
`quoted_price` int(6) DEFAULT NULL,
`current_price` int(6) DEFAULT NULL,
`original_interval` varchar(10) DEFAULT NULL,
`current_interval` varchar(7) DEFAULT NULL,
`temp_interval` int(5) NOT NULL,
`email` varchar(100) DEFAULT NULL,
`origin_date` varchar(20) DEFAULT NULL,
`remarks` text,
`crew_remarks` text NOT NULL,
`customer_type` varchar(10) DEFAULT NULL,
`perm_crew_assign` int(5) NOT NULL DEFAULT '0',
`temp_crew_assign` int(5) NOT NULL,
`date_last_service` date DEFAULT NULL,
`next_scheduled_date` date DEFAULT NULL,
`excluded_days` varchar(255) NOT NULL,
`special_instructions` text NOT NULL,
`inactive` tinyint(1) NOT NULL DEFAULT '0',
`location` varchar(255) NOT NULL,
`sent_letter` date NOT NULL,
`date_added` datetime NOT NULL,
`email_notify` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`account_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1687 DEFAULT CHARSET=utf8
CREATE TABLE `transactions` (
`transaction_id` int(10) NOT NULL AUTO_INCREMENT,
`account_id` int(6) NOT NULL,
`crew_id` int(3) NOT NULL,
`date_performed` date NOT NULL,
`date_recorded` datetime NOT NULL,
`price` int(6) NOT NULL,
`amount_paid` int(6) NOT NULL,
`description` varchar(255) NOT NULL,
`user` varchar(255) NOT NULL,
`status` int(1) NOT NULL DEFAULT '1',
PRIMARY KEY (`transaction_id`)
) ENGINE=InnoDB AUTO_INCREMENT=69233 DEFAULT CHARSET=latin1
You can do this with one query and one loop. You can join the two tables and sort the result. Then when the name changes, you know you are starting a new customer.
SELECT c.name, c.address, t.*
FROM customers AS c
JOIN transactions AS t ON (t.customer_id = c.customers.id)
WHERE c.owed > 0
ORDER BY `c`.`name`
// Data looks like this
// | customer.name | customer.address | transaction.* |
$prevName = null;
$prevCustTransList = [];
$prevCustTransTotalOwed = 0;
while ($row = result_array()) { // pseudo code
if ( $row['c.name'] != $prevName ) {
if ( $prevName != null && $prevCustTransTotalOwed > 0 ) {
echo $prevName; // pseudo code
foreach($prevCustTransList as $t) {
echo $t; // pseudo code
}
}
$prevName = $row['c.name'];
$prevCustTransTotalOwed = 0;
$prevCustTransList = [];
}
// Keep the list of transactions to print out later.
$prevCustTransList[] = transaction data; // pseudo code
// Keep a running total of the amount owed
// Replace with actual calculation
$prevCustTransTotalOwed += transaction.price;
}
Yes you can do this in one query, ordering by account_id. In your 'transactions' table account_id should be indexed, since you'll be using it to join to 'customer_data'.
$st_date = '2016-01-01';
$end_date = '2016-02-01';
//select sql
SELECT c.name,c.account_id, ts.*
FROM customer_data c
INNER JOIN transactions ts ON (c.account_id = ts.account_id)
WHERE ts.price > 0 and ts.date_performed between $st_date and $end_data
$current_account_id = null;
while ($row = $results) {
if ($current_account_id != $row['account_id']) {
//print customer name, details, etc..
}
//print the transactions detail
echo $results['transaction_id']; //etc...
//keep track of the current customer
$current_account_id = $row['account_id']
}
I am trying to generate invoice id in each invoice, now i am having thousands of invoices, Now while adding from different ip same time i am getting duplicate invoice ids how to prevent it,
invoice id generating by getting the last inserted invoice id and increment 1 to it.
my function as follows parameters
get_new_tbl_id('table_name','invoice_id_column','string to strip (INV in INV0012)','any conditions');
function get_new_tbl_id($tbl_name,$id_field,$string,$options='')
{
$new_id = 0;
$query_count_rows = "SELECT MAX(CONVERT(replace(replace($id_field,',',''),'$string',''), SIGNED INTEGER)) as $id_field FROM $tbl_name WHERE $id_field LIKE '$string%' $options";
$count_rows = mysql_query($query_count_rows);
$num_rows = mysql_num_rows($count_rows);
if($num_rows >0)
{
$last_row = mysql_fetch_assoc($count_rows);
$last_id = $last_row[$id_field];
$last_inserted_id = intval(str_replace($string,'',$last_id));
$new_id = $last_inserted_id+1;
}
else
$new_id = 1;
$format = '%1$03d';
$new_id=sprintf($format,$new_id,'');
return $string.$new_id;
}
My table as follows
CREATE TABLE IF NOT EXISTS `tbl_invoice` (
`invoice_tbl_id` int(11) NOT NULL AUTO_INCREMENT,
`invoice_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`invoice_ip` varchar(25) NOT NULL,
`invoice_status` tinyint(1) NOT NULL DEFAULT '0',
`invoice_added_by` smallint(6) NOT NULL,
`invoice_edited_by` smallint(6) NOT NULL,
`invoice_date` date NOT NULL,
`invoice_id` varchar(15) NOT NULL,
`customer_id` varchar(11) NOT NULL,
`invoice_credit_date` tinyint(4) NOT NULL,
`invoice_credit_status` tinyint(1) NOT NULL DEFAULT '0',
`total_items_count` smallint(6) NOT NULL,
`invoice_total_amount` varchar(20) NOT NULL,
`invoice_grandtotal_amount` double NOT NULL,
`invoice_discount` double NOT NULL DEFAULT '0',
`invoice_total_card_amount` double NOT NULL,
`invoice_total_cash_amount` double NOT NULL,
`invoice_total_profit` varchar(10) NOT NULL,
`cashier_approval` tinyint(1) NOT NULL DEFAULT '0',
`cashier_approval_id` smallint(6) NOT NULL,
`cashier_approval_time` datetime NOT NULL,
`cashier_approval_ip` varchar(20) NOT NULL,
`invoice_delete_note` text NOT NULL,
PRIMARY KEY (`invoice_tbl_id`),
KEY `invoice_id` (`invoice_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
Use a myisam table to generate the ids for you with 2 fields. The 1st field contains the prefix (this is $string in your function), the second should be an auto increment field. Add a primary key on these 2 fields, but the prefix field must be the 1st one in the index. If you insert a new row into this table with a prefix, then mysql will increment the auto increment value within that group.
See myisam notes section in mysql documentation on auto increment for details and example.
CREATE TABLE animals (
grp ENUM('fish','mammal','bird') NOT NULL,
id MEDIUMINT NOT NULL AUTO_INCREMENT,
name CHAR(30) NOT NULL,
PRIMARY KEY (grp,id)
) ENGINE=MyISAM;
INSERT INTO animals (grp,name) VALUES
('mammal','dog'),('mammal','cat'),
('bird','penguin'),('fish','lax'),('mammal','whale'),
('bird','ostrich');
If your base table is mysql, then just alter it to get this behaviour, if not, then create a separate myisam table, do the inserts into that one first, then obtain the ids fo use in your main table.
May there will be some optimized solution, but for now I can give you this solution
use static variable lock if one person is getting id make $lock=true and keep other requests on waiting for 1 second and check again by goto start; until first request is completed; make $lock=false; at the end to release the function.
public static $lock=false;
function get_new_tbl_id($tbl_name,$id_field,$string,$options='')
{
global $lock;
start:
if($lock==true){
sleep(1);
goto start;
}
if($lock==false){
$lock==true;
}
$new_id = 0;
$query_count_rows = "SELECT MAX(CONVERT(replace(replace($id_field,',',''),'$string',''), SIGNED INTEGER)) as $id_field FROM $tbl_name WHERE $id_field LIKE '$string%' $options";
$count_rows = mysql_query($query_count_rows);
$num_rows = mysql_num_rows($count_rows);
if($num_rows >0)
{
$last_row = mysql_fetch_assoc($count_rows);
$last_id = $last_row[$id_field];
$last_inserted_id = intval(str_replace($string,'',$last_id));
$new_id = $last_inserted_id+1;
}
else
$new_id = 1;
$format = '%1$03d';
$new_id=sprintf($format,$new_id,'');
$lock=false;
return $string.$new_id;
}
I have such table
CREATE TABLE IF NOT EXISTS `superTable` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`lotID` bigint(20) NOT NULL,
`characterID` bigint(20) NOT NULL,
`confirmChoice` varchar(255) DEFAULT NULL,
`confirmStatus` varchar(255) DEFAULT NULL,
`dateCreate` datetime DEFAULT NULL,
`dateStart` datetime DEFAULT NULL,
`dateEnd` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=0 ;
I need such order
First entries where confirmChoice is null.
Next entries where confirmChoice and dateStart is not null.
All next order by dateEnd;
How can i do it in one query?
If I understood your requirement correctly , this could be one way of achieving it :
SELECT
*
,CASE
WHEN (`confirmChoice` IS NULL) THEN '1'
WHEN (`confirmChoice` IS NOT NULL AND `dateStart` IS NOT NULL ) THEN '2'
ELSE '3'
END AS sort_order
FROM
`supertable`
WHERE
1
ORDER BY
sort_order
,`dateEnd`
You probably would need to tweak it to suit your requirement .
Abstract:
Every client is given a specific xml ad feed (publisher_feed table). Everytime there is a query or a click on that feed, it gets recorded (publisher_stats_raw table) (Each query/click will have multiple rows depending on the subid passed by the client (We can sum the clicks together)). The next day, we pull stats from an API to grab the previous days revenue numbers (rev_stats table) (Each revenue stat might have multiple rows depending on the country of the click (We can sum the revenue together)). Been having a hard time trying to link together these three tables to find the average RPC for each client for the previous day.
Table Structure:
CREATE TABLE `publisher_feed` (
`publisher_feed_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`alias` varchar(45) DEFAULT NULL,
`user_id` int(10) unsigned DEFAULT NULL,
`remote_feed_id` int(10) unsigned DEFAULT NULL,
`subid` varchar(255) DEFAULT '',
`requirement` enum('tq','tier2','ron','cpv','tos1','tos2','tos3','pv1','pv2','pv3','ar','ht') DEFAULT NULL,
`status` enum('enabled','disabled') DEFAULT 'enabled',
`tq` decimal(4,2) DEFAULT '0.00',
`clicklimit` int(11) DEFAULT '0',
`prev_rpc` decimal(20,10) DEFAULT '0.0000000000',
PRIMARY KEY (`publisher_feed_id`),
UNIQUE KEY `alias_UNIQUE` (`alias`),
KEY `publisher_feed_idx` (`remote_feed_id`),
KEY `publisher_feed_user` (`user_id`),
CONSTRAINT `publisher_feed_feed` FOREIGN KEY (`remote_feed_id`) REFERENCES `remote_feed` (`remote_feed_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `publisher_feed_user` FOREIGN KEY (`user_id`) REFERENCES `user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=124 DEFAULT CHARSET=latin1$$
CREATE TABLE `publisher_stats_raw` (
`publisher_stats_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`unique_data` varchar(350) NOT NULL,
`publisher_feed_id` int(10) unsigned DEFAULT NULL,
`date` date DEFAULT NULL,
`subid` varchar(255) DEFAULT NULL,
`queries` int(10) unsigned DEFAULT '0',
`impressions` int(10) unsigned DEFAULT '0',
`clicks` int(10) unsigned DEFAULT '0',
`filtered` int(10) unsigned DEFAULT '0',
`revenue` decimal(20,10) unsigned DEFAULT '0.0000000000',
PRIMARY KEY (`publisher_stats_id`),
UNIQUE KEY `unique_data_UNIQUE` (`unique_data`),
KEY `publisher_stats_raw_remote_feed_idx` (`publisher_feed_id`)
) ENGINE=InnoDB AUTO_INCREMENT=472 DEFAULT CHARSET=latin1$$
CREATE TABLE `rev_stats` (
`rev_stats_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`date` date DEFAULT NULL,
`remote_feed_id` int(10) unsigned DEFAULT NULL,
`typetag` varchar(255) DEFAULT NULL,
`subid` varchar(255) DEFAULT NULL,
`country` varchar(2) DEFAULT NULL,
`revenue` decimal(20,10) DEFAULT NULL,
`tq` decimal(4,2) DEFAULT NULL,
`finalized` int(11) DEFAULT '0',
PRIMARY KEY (`rev_stats_id`),
KEY `rev_stats_remote_feed_idx` (`remote_feed_id`),
CONSTRAINT `rev_stats_remote_feed` FOREIGN KEY (`remote_feed_id`) REFERENCES `remote_feed` (`remote_feed_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=58 DEFAULT CHARSET=latin1$$
Context:
Each remote_feed has a specific subid/typetag given to it. So we need to match up the both the remote_feed_id and the subid columsn from the publisher_feed table to the remote_feed_id and typetag columns in the revenue stats table.
My current, non working, implementation:
SELECT
pf.publisher_feed_id, psr.date, sum(clicks), sum(rs.revenue)
FROM
xml_network.publisher_feed pf
JOIN
xml_network.publisher_stats_raw psr
ON
psr.publisher_feed_id = pf.publisher_feed_id
JOIN
xml_network.rev_stats rs
ON
rs.remote_feed_id = pf.remote_feed_id
WHERE
pf.requirement = 'tq'
AND
pf.subid = rs.typetag
AND
psr.date <> date(curdate())
GROUP BY
psr.date
ORDER BY
psr.date DESC
LIMIT 1;
The above keeps pulling the wrong data out of the rev_stats table (pulls the sum of the correct stats, but repeats it over because of a join). Any help with how I would be able to properly pull the correct data would be greatly helpful ( I could use multiple queries and PHP to get the correct results, but what's the fun in that!)
Figured out a way to get this accomplished. Its def not a fast method by any means, needing 4 selects to get it done, but it works flawlessly =)
SELECT
pf.publisher_feed_id,
round(
(
SELECT
SUM(rs.revenue)
FROM
xml_network.rev_stats rs
WHERE
rs.remote_feed_id = pf.remote_feed_id
AND
rs.typetag = pf.subid
AND
rs.date = subdate(current_date, 1)
),10)as revenue,
(
SELECT
MAX(rs.tq)
FROM
xml_network.rev_stats rs
WHERE
rs.remote_feed_id = pf.remote_feed_id
AND
rs.typetag = pf.subid
AND
rs.date = subdate(current_date, 1)
) as tq,
(
SELECT
SUM(psr.clicks)-SUM(psr.filtered)
FROM
xml_network.publisher_stats_raw psr
WHERE
psr.publisher_feed_id = pf.publisher_feed_id
AND
psr.date = subdate(current_date, 1)
) as clicks
FROM
xml_network.publisher_feed pf
WHERE
pf.requirement = 'tq';
I need help extending a functional update query that performs calculations on one record to be able to perform calculations not only on one record in the database, but on all records associated with a particular user #.
Functionally, I need to extend an "edit this record" to "reevaluate all records of user#?"
Current calculations make use of 3 tables, sum a column, divide by the sum of another, and then creates a variable from that result(there are two columns summed separately to create two variables). Then I have a simple UPDATE query to update the record with the values of those variables. Each record has different values, and the sums will be different for every id#
#lastid is the unique record id.(allinfsds.id1)
I need to have the calculations done on all records that = a particular owners id (allinfsds.own_id)[i.e. WHERE allinfsds.own_id= usernum]
Any ideas???
Thanks ahead of time,
Nat
CREATE TABLE `allingred` (
`id6` int(8) NOT NULL auto_increment,
`usernum` varchar(255) default NULL,
`fsdsnum` int(8) unsigned zerofill NOT NULL,
`mfdfsds` varchar(255) default NULL,
`maybe` decimal(2,1) NOT NULL default '1.0',
`amount` float(10,2) default NULL,
`unit` int(6) default NULL,
`name` varchar(255) default NULL,
`wgt` int(9) NOT NULL,
PRIMARY KEY (`id6`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=38 ;
CREATE TABLE `weight` (
`NDB_No2` int(8) unsigned zerofill NOT NULL,
`Seq` smallint(6) NOT NULL,
`amt2` decimal(5,3) NOT NULL,
`Msre_Desc` varchar(80) NOT NULL,
`Gm_Wgt` decimal(7,1) NOT NULL,
`Num_Data_Pts` tinyint(4) default NULL,
`Std_Dev` decimal(7,1) default NULL,
`uni` int(7) NOT NULL auto_increment,
PRIMARY KEY (`uni`),
KEY `fb_join_NDB_No2_INDEX` (`NDB_No2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 AUTO_INCREMENT=21731 ;
CREATE TABLE `allinnot2` (
`NDB_No` int(8) unsigned zerofill NOT NULL,
`Water` decimal(10,2) default NULL,
`Energ_Kcal` decimal(10,0) default NULL
CREATE TABLE `allinfsds` (
`id1` int(8) unsigned zerofill NOT NULL,
`own_id` int(11) NOT NULL
UNIQUE KEY `id` (`id1`),
KEY `fb_groupbyorder_item_number_INDEX` (`item_number`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
SET #cal = (SELECT SUM( Energ_Kcal * allingred.amount * Gm_Wgt) / SUM( allingred.amount * Gm_Wgt ) AS nut100
FROM `allingred`
LEFT JOIN weight ON allingred.unit = weight.uni
LEFT JOIN allinnot2 ON allingred.mfdfsds = allinnot2.NDB_No
LEFT JOIN allinfsds ON allingred.fsdsnum = allinfsds.own_id
WHERE fsdsnum = #lastid)
SET #prot = (SELECT SUM(Protein * allingred.amount * Gm_Wgt) / SUM( allingred.amount * Gm_Wgt ) AS nut100
FROM `allingred`
LEFT JOIN weight ON allingred.unit = weight.uni
LEFT JOIN allinnot2 ON allingred.mfdfsds = allinnot2.NDB_No
WHERE fsdsnum = #lastid)
UPDATE `allinnot2` SET
`Energ_Kcal` = #cal,
`Protein` = #prot
WHERE `NDB_No` = #lastid
How to update multiple tuples at once
If you have a list of Ids you want to update, use
UPDATE `myTable` SET `myColumn` = 'newValue'
WHERE `userId` IN (
/*list of relevant Ids for instance: */ 15, 20, 63, 987
)
or if you dont have this list, but you can query the database for this list, use
UPDATE `myTable` SET `myColumn` = 'newValue'
WHERE `userId` IN (
SELECT `userId` FROM `myOtherTable` WHERE `relevantColumn` = 'value'
)
Beware that you are not allowed to use the same table as both the update target and source of ids in the subselect, so myTable != myOtherTable.