Select one row from one table and multiple rows from other table - php

I asked a little ago, but I find my question so bad, that it actually didn't help me do what I wanted, so I deleted it and tried again.
I have a table in my database called 'game' with the columns: k1, k1r, k2, k2r, k3, k3r, grp and week. The week-column is automatically inserted with the week of the year and the others are user-inputs from a betting game I made.
Then I have a table called 'matches' with the columns: match1, match2, match3, grp, week. Again, same procedure with the week-column. The other 4 I fill out with the matches we should bet on. The 'grp' column in both is filled by me with 'BS' and 'VF'.
I then have a query:
SELECT *
FROM game
WHERE week = '.$week.' AND grp = 'bs';
$week is already set as $week = Date('W');
It outputs a table, where I write the matches in 3 columns and then echoes the users bets in the rows under the matches. But instead of me changing the php-script every time there are new matches, I would like it to take them from the table 'matches'. In that way, I also keep the old matches in the database.
So, now to my question :)
What I would like to do is something like:
'SELECT * FROM game WHERE week = '.$week.' AND grp = 'bs'
JOIN * FROM matches WHERE week = '.$week.' AND grp = 'bs';
but as you probably now, that won't work :/
In my other question I forgot to mention the 'grp' column and couldn't figure out how to insert it.
To echo it I used:
while ($row = mysqli_fetch_array($query)) {
$no = 1;
$amount = $row['amount'] == 0 ? '' : number_format($row['amount']);
echo '<tr>
<td><strong>'.$row['name'].'</strong></td>
<td>'.$row['k1']."<br />".$row['k1r'].'</td>
<td>'.$row['k2']."<br />".$row['k2r'].'</td>
<td>'.$row['k3']."<br />".$row['k3r'].'</td> }
but I would like an outcome like this:
| |Match1 |Match2 |Match3 |
|User1 |2-0 |2-1 |2-2 |
|User2 |1-1 |2-2 |1-2 |
And so on ... where the 'Matches' are selected from the 'matches'-table and the bets are selected from the 'game'-table. So I just need the one row from 'Matches' as the headline and then loop through the rows in 'Game'.
The creates are:
CREATE TABLE `game`
(
`id` int(11) NOT NULL AUTO_INCREMENT,
`uid` int(11) DEFAULT NULL,
`user` varchar(20) COLLATE utf8_danish_ci DEFAULT NULL,
`name` varchar(50) COLLATE utf8_danish_ci DEFAULT NULL,
`k1` varchar(1) COLLATE utf8_danish_ci DEFAULT NULL,
`k1r` varchar(10) COLLATE utf8_danish_ci DEFAULT NULL,
`k2` varchar(1) COLLATE utf8_danish_ci DEFAULT NULL,
`k2r` varchar(10) COLLATE utf8_danish_ci DEFAULT NULL,
`k3` varchar(1) COLLATE utf8_danish_ci DEFAULT NULL,
`k3r` varchar(10) COLLATE utf8_danish_ci DEFAULT NULL,
`week` int(2) DEFAULT NULL,
`grp` varchar(11) COLLATE utf8_danish_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_index` (`uid`,`week`,`grp`)
) ENGINE=MyISAM AUTO_INCREMENT=32 DEFAULT CHARSET=utf8 COLLATE=utf8_danish_ci
And:
CREATE TABLE `udvalgte_kampe`
(
`hold1` varchar(50) COLLATE utf8_danish_ci DEFAULT NULL,
`hold2` varchar(50) COLLATE utf8_danish_ci DEFAULT NULL,
`hold3` varchar(50) COLLATE utf8_danish_ci DEFAULT NULL,
`hold4` varchar(50) COLLATE utf8_danish_ci DEFAULT NULL,
`hold5` varchar(50) COLLATE utf8_danish_ci DEFAULT NULL,
`hold6` varchar(50) COLLATE utf8_danish_ci DEFAULT NULL,
`week` int(2) DEFAULT NULL,
`grp` varchar(11) COLLATE utf8_danish_ci DEFAULT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_danish_ci
But what should I do then?

Be aware for sql injection using $var and you can avoid string concatenation using proper quotes.
your second query the sintax for join is
is wrong youn should use this sintax
"SELECT * FROM game
JOIN matches on game.week = matches.week
where WHERE game.week = '$week' AND game.grp = 'bs'";
and not
'SELECT * FROM game WHERE week = '.$week.' AND grp = 'bs'
JOIN * FROM matches WHERE week = '.$week.' AND grp = 'bs';

If you use the SQL from the previous answer, I think this is roughly what you would need in your PHP :
$headerPrinted = $false;
while ($row = mysqli_fetch_array($query)) {
if (!headerPrinted) {
echo '<th>
<td></td>
<td>'.$row['hold1'].'</td>
<td>'.$row['hold2'].'</td>
<td>'.$row['hold3'].'</td>
</th>';
$headerPrinted = $true;
}
$no = 1;
$amount = $row['amount'] == 0 ? '' : number_format($row['amount']);
echo '<tr>
<td><strong>'.$row['name'].'</strong></td>
<td>'.$row['k1']."<br />".$row['k1r'].'</td>
<td>'.$row['k2']."<br />".$row['k2r'].'</td>
<td>'.$row['k3']."<br />".$row['k3r'].'</td>';
}
I'm making the assumption that hol1, hold2 and hold3 are the columns you want to use for your header.
I'm not saying this is the best way to achieve what you want, but it's probably good enough for your current level of experience.

Related

Creating an invoice from SQL query without putting query in a loop

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']
}

Optimize a count SQL query on a big table

I have a table with over 10 thousand registers right now, and they start to run so slow.
I have the following code:
COUNT
$SqlCount = "SELECT tabnews.New_Id
FROM tabnew WHERE New_Id <> '' AND New_Status = 1";
$QueryCount = mysql_query($SqlCount, $Conn) or die(mysql_error($Conn));
$NumCount = mysql_num_rows($QueryCount);
$recordCount = $NumCount;
PAGINATION
if (!$id) $p = 1;
else $p = $id;
$pageSize = 16;
$itemIni = ($pageSize*$p)-$pageSize;
$totalPage = ceil($recordCount/$pageSize);
SHOW
$Sql52 = "SELECT New_Id, New_Nome, New_Data, New_Imagem FROM tabnews WHERE New_Status = 1 ORDER BY New_Id DESC LIMIT $itemIni, $pageSize ";
$Query52 = mysql_query($Sql52, $Conn);
while($Rs52 = mysql_fetch_array($Query52)){
// ECHO RESULTS
}
MY DATABASE:
CREATE TABLE IF NOT EXISTS `tabnews` (
`New_Id` int(11) NOT NULL AUTO_INCREMENT,
`Franquia_Id` text NOT NULL,
`New_Slide` int(2) NOT NULL,
`Categoria_Id` int(2) NOT NULL,
`New_Nome` varchar(255) NOT NULL,
`New_Data` date NOT NULL,
`New_Imagem` varchar(75) NOT NULL,
`New_Status` int(11) NOT NULL,
PRIMARY KEY (`New_Id`),
KEY `idx_1` (`New_Status`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=10490 ;
Any ideas on how I can make this run faster?
I have a dedicated server running CENTOS.
This:
New_Id <> ''
What does this do? It casts every single one of your INT primary key to string to compare it to a string. Why would you compare it to a string? It cannot be '' by definition, omit that New_Id <> '' from your WHERE clause.
20 seconds is very weird for such a little table.
I have a very similar table with almost 4 million rows and your both SQL statements takes less than 0.002 sec.
CREATE TABLE IF NOT EXISTS `tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`status` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'open',
`method` varchar(10) COLLATE utf8_unicode_ci NOT NULL DEFAULT 'GET',
`url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`params` text COLLATE utf8_unicode_ci,
`response` text COLLATE utf8_unicode_ci,
`executed_by` varchar(50) COLLATE utf8_unicode_ci DEFAULT '',
`execute_at` datetime DEFAULT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `status` (`status`),
KEY `modified` (`modified`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=3839270 ;
-
SELECT COUNT(id) FROM tasks WHERE status='done';
---> Query took 0.0008 sec
-
SELECT id, status, method, url FROM tasks WHERE status='done' ORDER BY id DESC LIMIT 200, 100;
---> Query took 0.0011 sec
Observations:
You should use SELECT COUNT(New_Id)...
New_id <> '' doesn't make sense. New_id can't be empty or NULL
Set the length of New_Status to something that match the values you store there
Try turning off logging: SET GLOBAL general_log = 'OFF';
Update your server packages (specially MySQL)
Is it a dedicated server only for the database?
Is the server running other things? (run 'top' and 'uptime' to check it status)

Find posts of user grouped by month, unixtime

I'd like to find the number of posts for each user grouped by month.
I'm currently using INT(10) unsigned to store the date of posts.
what would be a super fast way to do this?
CREATE TABLE IF NOT EXISTS `media` (
`pid` int(10) unsigned NOT NULL AUTO_INCREMENT,
`class` tinyint(1) NOT NULL DEFAULT '1',
`date_class_changed` int(10) unsigned NOT NULL,
`title` char(5) CHARACTER SET latin1 COLLATE latin1_general_cs NOT NULL,
`url` varchar(1024) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`media` enum('image','video') CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`thumb` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`description` varchar(140) COLLATE utf8_unicode_ci NOT NULL,
`username` varchar(16) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`date` int(10) unsigned NOT NULL,
`file` varchar(1024) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`hash` char(32) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`hashtag` text CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`meta` varchar(255) CHARACTER SET latin1 COLLATE latin1_general_ci NOT NULL,
`ip` int(10) unsigned NOT NULL,
`kind` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`pid`),
UNIQUE KEY `title` (`title`),
KEY `hash` (`hash`),
KEY `class_date` (`class`,`date_class_changed`),
KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=1022724 ;
This is the table, I'm talking about, I'd like to display the number of posts for each user for each mont, such as: september 2012, User X, N posts etc..
The query I'm using after the help from #fthiella is:
SELECT
DATE_FORMAT(FROM_UNIXTIME(`date`), '%Y-%m') as YearMonth, username, COUNT(*) as Posts
FROM
media
WHERE username = 'foobar'
GROUP BY 1
ORDER BY 1 DESC
thanks God it's fast enough, now I'll try to optimize in case it's not using an index, but for now with almost 1M record is doing good. cheers.
SELECT
DATE_FORMAT(FROM_UNIXTIME(`date`), '%Y-%m') as YearMonth,
username,
COUNT(*) as Posts
FROM
media
GROUP BY
DATE_FORMAT(FROM_UNIXTIME(`date`), '%Y-%m') as YearMonth,
username

Retrieve Data from Database and Count the number of times the data occurs

I have a couple of tables in MySQL that I'm working with that do not have any relation. They do have a column of similar data (Postal Codes / Zip Codes).
What I have to do with these tables is compare the postal codes from one table compare them to the postal codes from the first table and count them.
For Example.
Table A has a postal code of T0A and T0B (I use only the first three characters in the postal code as this is all I need to compare against)
Table B has 13 rows where the postal code matches T0A and 3 rows where the postal code matches T0B.
So the outcome would look like:
T0A = 13
T0B = 3
HOWEVER, then I need to take these and separate them by city, so since both T0A and T0B could be one city I would need to take those and add them together and get something like.
Edmonton = 16
I've been doing this with for loops and arrays. So I'm reading the data from table A into one array and the data from table b into another array. Then I compare the postal codes from table B to the postal codes in table A using nested for loops in order to count the number of occurrences of the postal codes and then I store them in another array. This is all fine and dandy however now I'm a bit stuck trying to separate the counts into their correct cities and I'm sitting here thinking there must be an easier way to do this. Does anyone have any suggestions, am I going about this all wrong?
Structure - Table A
jos_postalzip_redirect | CREATE TABLE `jos_postalzip_redirect` (
`id` int(11) NOT NULL auto_increment,
`country_code` varchar(2) NOT NULL,
`prov_state_code` varchar(2) NOT NULL,
`city` varchar(60) NOT NULL,
`postal_zip` varchar(6) NOT NULL,
`email_address` varchar(60) default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=739 DEFAULT CHARSET=utf8 |
Structure - Table B
jos_form_submitteddata_form1 | CREATE TABLE `jos_form_submitteddata_form1` (
`id` int(11) NOT NULL auto_increment,
`bf_status` varchar(20) collate utf8_bin NOT NULL,
`bf_user_id` int(11) NOT NULL,
`FIELD_1` varchar(255) collate utf8_bin NOT NULL,
`FIELD_2` varchar(255) collate utf8_bin NOT NULL,
`FIELD_3` varchar(255) collate utf8_bin NOT NULL,
`FIELD_4` varchar(255) collate utf8_bin NOT NULL,
`FIELD_5` varchar(255) collate utf8_bin NOT NULL,
`FIELD_6` varchar(255) collate utf8_bin NOT NULL,
`FIELD_7` varchar(255) collate utf8_bin NOT NULL,
`FIELD_8` varchar(255) collate utf8_bin NOT NULL,
`FIELD_23` varchar(255) collate utf8_bin NOT NULL,
`FIELD_24` varchar(255) collate utf8_bin NOT NULL, //THIS IS THE POSTAL CODE FIELD
`FIELD_28` varchar(255) collate utf8_bin NOT NULL,
`FIELD_29` varchar(255) collate utf8_bin NOT NULL,
`FIELD_30` varchar(255) collate utf8_bin NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=4044 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
Just an abstraction of what I understood. You may need to adjust it according to your needs.
In this example I will assume that FIELD_1 in table B is a postal code.
Count by postal code:
select
left(ta.postal_zip, 3) p_code, count(*)
from
jos_form_submitteddata_form1 tb
join jos_postalzip_redirect ta on left(tb.field_1, 3) = left(ta.postal_zip, 3)
group by
p_code
Count by city:
select
ta.city, count(*)
from
jos_form_submitteddata_form1 tb
join jos_postalzip_redirect ta on left(tb.field_1, 3) = left(ta.postal_zip, 3)
group by
ta.city

Selecting rows that corresponds from other table

I have a product table that stores all products. Also I have a production table that stores productions.
I am using CodeIgniter and datamapper ORM.
Here is tables:
CREATE TABLE IF NOT EXISTS `products` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`kod_stok` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL,
`kod_lokal` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL,
`kod_firma` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
`firma` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL,
`fabrika` varchar(10) COLLATE utf8_unicode_ci NOT NULL,
`proje` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
`tanim` mediumtext COLLATE utf8_unicode_ci,
`saatlik_uretim` int(11) NOT NULL,
`status` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `kod_lokal` (`kod_lokal`),
KEY `kod_firma` (`kod_firma`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
CREATE TABLE IF NOT EXISTS `productions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`fabrika` varchar(15) COLLATE utf8_unicode_ci DEFAULT NULL,
`board_no` int(11) NOT NULL,
`date` int(11) DEFAULT NULL, // Unix Timestamp
`operator_id` int(11) DEFAULT NULL,
`product_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `product` (`product_id`),
KEY `date` (`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ;
I am trying to get count of production of given day. But not all products, producting everyday. I need to exlude the products that has 0 count.
$p = new Product();
$p->include_related_count('production');
$p->get();
And I want to add a date interval to production.
Basicly, I want to get all product's production count within a given day.
How can I do that?
Thank you for any advices.
Not sure about codeigniter details, but the following SQL query will generate a production list per day.
To get today's production:
$query = $this->db->query("
SELECT
a.count(*) as produced
, a.product_id
, b.kod_stok as productname
FROM productions a
INNER JOIN products b ON (a.product_id = b.id)
WHERE FROM_UNIXTIME(a.date) = CURDATE()
GROUP BY TO_DAYS(FROM_UNIXTIME(a.date)), a.product_id
");
To get last 7 days production
$query = $this->db->query("
SELECT
a.count(*) as produced
, a.product_id
, b.kod_stok as productname
FROM productions a
INNER JOIN products b ON (a.product_id = b.id)
WHERE FROM_UNIXTIME(a.date)
BETWEEN DATE_SUB(CURDATE(),INTERVAL 7 DAY) AND CURDATE()
GROUP BY TO_DAYS(FROM_UNIXTIME(a.date)), a.product_id
");

Categories