title: memory usage export from database to csv in php
body: <p>I need to export data from mysql to csv. I have to select data from several tables putting them in arrays, then process them and return them to browser as .csv. 
  I noticed that arrays consume huge ammount of rows.
  For example, I imported a .csv in the database which is 1.8M, then I try to export this data from the database in .csv. The memory_get_peak_usage() shows more than 128M to store arrays with data.</p>
  
  <p>For example this small array takes more than 700 bytes:</p>
  
  <pre><code>$startMemory = memory_get_usage();  
          //get constant fields of the subscriber
          $data = array(array('subscriber_id' =&gt; 1315444, 'email_address' =&gt; 'test0@gmail.com',
                              'first_name' =&gt; 'Michael', 'last_name' =&gt; 'Allen'));
          echo memory_get_usage() - $startMemory;
  </code></pre>
  
  <p>So exporting even several megabytes of data, require hundreds megabytes of memory in php script.
  Is there a way to solve this problem? 
  Tables:</p>
  
  <pre><code>    CREATE TABLE `subscribers` (
       `subscriber_id` int(10) unsigned NOT NULL auto_increment,
       `list_id` int(10) unsigned NOT NULL,
       `account_id` int(10) unsigned NOT NULL,
       `email_address` varchar(100) collate utf8_unicode_ci NOT NULL,
       `first_name` varchar(50) collate utf8_unicode_ci NOT NULL default '',
       `last_name` varchar(50) collate utf8_unicode_ci NOT NULL default '',
       `ip` int(10) unsigned default NULL COMMENT '\nThe ip address of the subscriber that we can get when he opens the \nthe email or subscribe using subsribe form.\nTheoretically it can be used to segment by Location (which is not correct if someone uses proxy).',
       `preferred_format` tinyint(4) NOT NULL default '0' COMMENT 'Preferred format of \n0 - HTML, \n1 -Text,\n2 - Mobile',
       `state` tinyint(4) NOT NULL default '1' COMMENT '1 - subscribed\n2 - unsubscribed\n3 - cleaned\n4 - not confirmed, it means the user subscribed but has not confirmed it yet.\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n',
       `cause_of_cleaning` tinyint(4) NOT NULL default '0' COMMENT '\nThis field is the cause of moving the subscriber to the \n0 - not used\n1 - spam complaint\n2 - hard bounce\n3 - several soft bounces',
       `date_added` datetime NOT NULL COMMENT 'The data when the subscriber was added. I suppose this field can be used in the conditions forming the segment',
       `last_changed` datetime NOT NULL,
       PRIMARY KEY  (`subscriber_id`),
       UNIQUE KEY `email_list_id` (`email_address`,`list_id`),
       KEY `FK_list_id` (`list_id`),
       CONSTRAINT `FK_list_id` FOREIGN KEY (`list_id`) REFERENCES `lists` (`list_id`) ON DELETE CASCADE ON UPDATE CASCADE
      ) ENGINE=InnoDB 
      CREATE TABLE `subscribers_multivalued` (
       `id` int(10) unsigned NOT NULL auto_increment,
       `subscriber_id` int(10) unsigned NOT NULL,
       `field_id` int(10) unsigned NOT NULL,
       `value` varchar(100) collate utf8_unicode_ci NOT NULL,
       `account_id` int(10) unsigned NOT NULL COMMENT '\nThe identifier of the account',
       PRIMARY KEY  (`id`),
       KEY `subscriber_fk` (`subscriber_id`),
       KEY `field_fk` (`field_id`),
       CONSTRAINT `field_fk_string_multivalued` FOREIGN KEY (`field_id`) REFERENCES `custom_fields` (`field_id`) ON DELETE CASCADE ON UPDATE CASCADE,
       CONSTRAINT `subscriber_fk_multivalued` FOREIGN KEY (`subscriber_id`) REFERENCES `subscribers` (`subscriber_id`) ON DELETE CASCADE ON UPDATE CASCADE
      ) ENGINE=InnoDB
  CREATE TABLE `subscribers_custom_data_string` (
   `subscriber_id` int(10) unsigned NOT NULL,
   `field_id` int(10) unsigned NOT NULL,
   `value` varchar(255) collate utf8_unicode_ci NOT NULL,
   `account_id` int(10) unsigned NOT NULL COMMENT '\nThe identifier of the account',
   PRIMARY KEY  (`subscriber_id`,`field_id`),
   KEY `subscriber_fk` (`subscriber_id`),
   KEY `field_fk` (`field_id`),
   CONSTRAINT `field_fk_string` FOREIGN KEY (`field_id`) REFERENCES `custom_fields` (`field_id`) ON DELETE CASCADE ON UPDATE CASCADE,
   CONSTRAINT `subscriber_fk_string` FOREIGN KEY (`subscriber_id`) REFERENCES `subscribers` (`subscriber_id`) ON DELETE CASCADE ON UPDATE CASCADE
  ) 
  </code></pre>
  
  <p>There are other tables for fields similar to the table with strings for numbers, dates. For them primary key is subscriber_id, field_id.</p>
  
  <p>When query fails (for example we have several custom fields): </p>
  
  <p>SELECT <code>subscribers</code>.<code>email_address</code>, <code>subscribers</code>.<code>first_name</code>, <code>subscribers</code>.<code>last_name</code>,
  GROUP_CONCAT(t1.value SEPARATOR '|') AS Colors, GROUP_CONCAT(t2.value SEPARATOR '|') AS Languages
  FROM <code>subscribers</code>
  LEFT JOIN <code>subscribers_multivalued</code> AS <code>t1</code> ON subscribers.subscriber_id=t1.subscriber_id AND t1.field_id=112
  LEFT JOIN <code>subscribers_multivalued</code> AS <code>t2</code> ON subscribers.subscriber_id=t2.subscriber_id AND t2.field_id=111
  WHERE (list_id=40)
  GROUP BY <code>subscribers</code>.<code>email_address</code>, <code>subscribers</code>.<code>first_name</code>, <code>subscribers</code>.<code>last_name</code></p>
  
  <p>It would return this:</p>
  
  <p>test1000@gmail.com      Michelle    Bush    Red|Red|Blue|Blue   English|Spanish|English|Spanish instead of
  test1000@gmail.com      Michelle    Bush    Red|Blue
  English|Spanish</p>
  
  <p>Thank you for any information.</p>