# My-SQL Abfrage - Out Of Memory Error



## Bazilinho (30. Jan 2012)

Hi!
Ich hab das Problem, dass meine MySQL-Datenbank-Abfrage meinen Heap-Space sprengt.. 
...dabei habe ich Ihn schon auf 4096 MB gepusht.. 

Vielleicht hat einer ne Ahnung warum.. Wäre für nen Tipp sehr dankbar!! Hatte erst den Verdacht, dass die ResultSets im Speicher bleiben und da es sich ja um ne doppelte for-Schleife handelt und ich zuerst auch die ResultSets und Statements nicht geschlossen hatte. Nun schleiße ich Sie aber beide, wobei das RS ja automatisch mit dem Statement geschlossen werden müßte!?! ???:L

Gut, es ist ev nicht die schnellste Implementierung, aber so wie ich es gelöst habe ,arbeitet es zumindest korrekt.. 

Kurz zur Erklärung: Da ich mehrere Abfrage etc mit der DB mache wird die Verbindung einmal zum Programmstart hergestellt und bleibt dann geöffnet.
ICh habe meine DB-Spalten nummeriert, daher die SAchen mit 05,06,...


Ich pack den Code mal unten ran..

Vielen Dank schonmal!!


```
try {
			
			uhrzeit = sdd.format(new Date());
			filo_g.log.insert(uhrzeit+"  now computing colours for hit and prio on rank (without prepaid)! \n",0);
			
	
			
			for (int rank_nr = 1; rank_nr <= 10; rank_nr++) {
			
				String column_nr = "";
				
				if (rank_nr == 1) column_nr = "05";
				else if (rank_nr == 2) column_nr = "06";
				else if (rank_nr == 3) column_nr = "07";
				else if (rank_nr == 4) column_nr = "08";
				else if (rank_nr == 5) column_nr = "09";
				else if (rank_nr == 6) column_nr = "10";
				else if (rank_nr == 7) column_nr = "11";
				else if (rank_nr == 8) column_nr = "12";
				else if (rank_nr == 9) column_nr = "13";
				else if (rank_nr == 10) column_nr = "14";
				
				for (int usages_count = 0; usages_count < usages.length; usages_count++) {
					
					Statement stmt_h_p_green = null;
					ResultSet rs_h_p_green;
					String query_h_p_green;
					stmt_h_p_green = conn_results.createStatement();
					query_h_p_green = "SELECT vv_03_plz, vv_09_carrier_id FROM `powerdata`.`vv_data` where vv_00_date_of_import = '1111-11-11' AND vv_02_usage = '"+usages[usages_count]+"' AND vv_31_rank_postpaid = '"+rank_nr+"' group by vv_03_plz;";
					
					rs_h_p_green = stmt_h_p_green.executeQuery(query_h_p_green);
					
					ArrayList<Integer> h_p_green_zips = new ArrayList<Integer>(); 
					rs_h_p_green.beforeFirst();
					
					while(rs_h_p_green.next()) {
						if (rs_h_p_green.getInt(2) == 6600 || rs_h_p_green.getInt(2) == 6653) {
							h_p_green_zips.add(rs_h_p_green.getInt(1));
						}
					}

					conn_results.setAutoCommit(false);
					
					rs_h_p_green.close();
					stmt_h_p_green.close();
					PreparedStatement prest_hp = null;
					
					Iterator iterate = h_p_green_zips.iterator(); 
				
					while (iterate.hasNext()) {
						String prep_sql = "UPDATE plz_result_"+usages[usages_count]+" SET result_"+usages[usages_count]+"_"+column_nr+"_rank"+rank_nr+" = ? WHERE result_"+usages[usages_count]+"_01_plz ='"+iterate.next().toString()+"'";
						prest_hp = (PreparedStatement) conn_results.prepareStatement(prep_sql);
						prest_hp.setString(1, "T_00_HIT_and_Prio");
						prest_hp.addBatch();
						int[] update_counts_colours = prest_hp.executeBatch();
					}
					h_p_green_zips.clear();
					conn_results.commit();
					conn_results.setAutoCommit(true);
					prest_hp.clearBatch();
					
					uhrzeit = sdd.format(new Date());
					filo_g.log.insert(uhrzeit+"  Colour for HIT and Prio were set for usage "+usages[usages_count]+"   and rank "+rank_nr+"!! \n",0);
				}
			}
		}
		catch (Exception e) {
			uhrzeit = sdd.format(new Date());
			filo_g.log.insert(uhrzeit+"  error when setting hit and prio top ranks to green! "+e.toString()+" \n",0);
			System.out.println("Fehler beim Faerben der Top Positions von Hit und Prio "+e.toString());
		}
```





Gruß, Bazizilinho


----------



## AlexSpritze (30. Jan 2012)

Eine allgemeine Verbesserung wäre auf jeden Fall in Zeile 29 Prepared Statement ? Wikipedia zu nutzen. Das scheinst du in Zeile 51 ja schon irgendwie zu machen. Aber trotzdem baust du den SQL-String händisch zusammen.


----------



## Evil-Devil (30. Jan 2012)

Die Ergebnismenge von deinem Select solltest du idealerweise bereits auf 6600 und 6653 eingrenzen. Dann sparst du dir das IF und jede Menge Daten die nicht benötigt werden. Oder werden die an anderer Stelle noch verarbeitet? Sehe jedenfalls nichts dazu im Code.

Allgemein sieht deine SQL Anweisung im ganzen eher so aus als wenn man sie für SQL automatisieren könnte und alles in ein konfigurierbares SQL Skript geben sollte.


----------



## Bazilinho (30. Jan 2012)

Nee, das ist korrekt! 
Vielleicht sollte ich wirklich nochmal den Ansatz ausprobieren und das in eine Stroed Procedure einarbeiten.. 

Ansonsten ist es so, dass ich mir wirklich einfach immer die entsprechende Nummer anzeigen lasse, handelt es sich um 6600 / 6653 mache ich "weiter" und sonst passiert nix...


----------



## Evil-Devil (30. Jan 2012)

Was genau willst du denn machen? Vielleicht kann man das noch anders lösen.


----------



## Bazilinho (30. Jan 2012)

Also in meiner DB habe ich in der entsprechenden Tabelle u.A. eine Spalte die mir ein Ranking pro PLZ anzeigt. Konkret habe ich verschiedene ID's und die können mal auf Platz 1,2,3,... stehen.
Dazu gibts noch verschiedene Einteilungen, also 1500,2000,...

Und mich interessieren jeweils nur die Rankings von den ID's  6600 und 6653.

Zunächst interessiert mich bei welchen PLZ die beiden ID's auf Platz 1 stehen, jeweils in den Einteilungen 1500,2000,...
Danach das Gleiche für Platz 2, etc... 

An den entsprechenden Stellen möchte ich nun einen String in die Datenbank speichern.. Dieser ist oben in Zeile 53 repräsentiert..

Auf den Code bezogen bedeutet das:

Die erste for-schleife geht also von 1-10 Rang für Rang durch,
die zweite (innere) for-schleife geht von 1500,... meine verschiedenen Einteilungen durch. 

Ich lasse mir jeweils das Tupel aus PLZ und der ID des Rangs und der Einteilung geben. Ist die ID = 6600, bzw 6653 werden die PLZ in meine Array-List geschrieben. Anschließend wird meine entsprechende Tabelle (habe hier eine Tabelle pro Einteilung!!) an den ermittelten PLZs mit dem String geupdated...

Gruß, Sebastian


----------



## HimBromBeere (30. Jan 2012)

> ```
> if (rank_nr == 1) column_nr = "05";
> else if (rank_nr == 2) column_nr = "06";
> else if (rank_nr == 3) column_nr = "07";
> ...


Das geht doch auch kürzer... wie wär´s z.B. mit 
	
	
	
	





```
column_nr = rank_nr + 4;
```
?


----------



## Bazilinho (30. Jan 2012)

... und schonmal vielen Dank für die Hilfe soweit!!!  :toll:


----------



## Evil-Devil (30. Jan 2012)

Also dann sieht deine Tabelle intern ungefähr wie folgt aus:


```
vv_03_plz | vv_09_carrier_id | vv_00_date_of_import |  vv_02_usage | vv_31_rank_postpaid
22359 | 1234 | 1111-11-11 | 1500 | 1
20095 | 6600 | 1111-11-11 | 2000 | 2
22359 | 5678 | 1111-11-11 | 2000 | 3
20095 | 6653 | 1111-11-11 | 1500 | 4
```

richtig? Um sich mal einen inhaltlichen Überblick zu verschaffen.

Wenn dich aber ohnehin nur die 6600 und 6653 interessieren, dann könntest du dein Select doch trotzdem bereits auf die beiden carrier ids begrenzen oder nicht?


----------



## Bazilinho (30. Jan 2012)

Ja, genau!
So siehts ungefähr aus!! 

Ja, macht vermutlich Sinn, dass ich das SELECT dann umbaue.. Hatte gedacht / gehofft, dass es effizienter wird, wenn ich mir pro vv_02_usage (also 1500,2000,...) einfach erstmal die plz hole, die rausfiltere, die ich updaten will und das dann über dieses batch-command laufen lasse.. 

Aber ist natürlich richtig, dass ich statt mir alle PLZ eins Resultset zu holen auch mit ner Untermenge arbeiten kann...


----------



## Evil-Devil (30. Jan 2012)

Naja, an sich sollte man das auf ungefähr ein Statement reduzieren können. Die Bedingungen sind zwar variabel aber fest definiert.

ohne gewähr:
[sql]
select vv_03_plz, vv_09_carrier_id, vv_02_usage, vv_31_rank_postpaid
from vv_data
where vv_00_date_of_import = '1111-11-11'
and (vv_09_carrier_id = 6600 or vv_09_carrier_id = 6653)
and vv_31_rank_postpaid between 1 and 10
group by vv_09_carrier_id, vv_31_rank_postpaid, vv_02_usage
[/sql]

Wenn ich da inhaltlich keinen Fehler eingebaut habe, dann solltest du für die beiden Carrier die Rankings 1 bis 10  unterteilt in die Usages erhalten.

Falls es sonst möglich ist gib uns ein paar Zeilen aus der Tabelle mit der man kurzer Hand selbst hantieren kann. Idealerweise inklusive Table Create Statement


----------



## Bazilinho (30. Jan 2012)

Abermals vielen Dank!!

Ich habe Dein Statement mal minimal abgeändert: 


```
SELECT vv_03_plz, vv_09_carrier_id, vv_02_usage, vv_31_rank_postpaid
FROM vv_data
WHERE vv_00_date_of_import = '1111-11-11'
AND (vv_09_carrier_id = 6600 OR vv_09_carrier_id = 6653)
AND vv_02_usage = 1500
AND vv_31_rank_postpaid = 1
GROUP BY vv_03_plz
```

Das ist ja das was ich brauche...
Ich denke, wenn ich das über ne Stored Procedure laufen lassen und dort rank und die usage als parameter übergebe, lasse ich einfach die entsprechende Prozedur durch die forschleifen laufen. Weil - ganz ehrlich - das mit den Schleifen in MySQLl ist noch nicht so ganz meins... 

Letztlich baue ich das dann ein UPDATE SELECT ein..

Naja, die Tabelle.. Also prinzipiell hast Du das alles schon erfasst.. In meiner Tabelle stehen zwar noch andere Werte, aber die fasse ich ja überhaupt nicht an. 

Ich hänge einfach nochmal nen reduziertes create-statement an.. :rtfm:


```
CREATE TABLE `vv_data` (
  `vv_00_date_of_import` date NOT NULL,
  `vv_01_id` int(11) NOT NULL AUTO_INCREMENT,
  `vv_02_usage` mediumint(9) DEFAULT NULL,
  `vv_03_plz` int(11) DEFAULT NULL,
  `vv_04_ort` varchar(45) DEFAULT NULL,
  `vv_05_bundesland` varchar(45) DEFAULT NULL,
  `vv_06_xxxxx` mediumint(9) DEFAULT NULL,
  `vv_07__rank` smallint(6) DEFAULT NULL,
  `vv_08_xxxxx` varchar(45) DEFAULT NULL,
  `vv_09_carrier_id` smallint(6) DEFAULT NULL,
  `vv_10_carrier_name` varchar(45) DEFAULT NULL,
 
   .......

  `vv_31_rank_postpaid` int(11) DEFAULT NULL,
  `vv_32_rank_prepaid` int(11) DEFAULT NULL,
  PRIMARY KEY (`vv_01_id`),
  KEY `PLZ_index` (`vv_03_plz`),
  KEY `Date_index` (`vv_00_date_of_import`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8$$
```

Dazu die Zeilen:



1111-11-11	1	1500	1067	Dresden	Sachsen	6500	1	321,00	6718		309945		33,00	0,00	0,00	58,80	000000		12 Months	0,00	1	01.12.2011		163,20	1200	0	0	1	10.12.2011	1	1       	0
1111-11-112	2	1500	1067	Dresden	Sachsen	6500	2	329,15	6600	Name 6600	309979	String blablabla	20,69	0,00	109,72	128,52	000000		24 Months	0,00	0	09.12.2011		0,00	0	0	0	1	10.12.2011	1	2	0
1111-11-11	3	1500	1067	Dresden	Sachsen	6500	3	329,94	6669	Name 6669 	309437	blabla	20,69	0,00	65,00	84,59	000000		3 Months	0,00	0	05.12.2011		0,00	0	0	0	0	10.12.2011	1	3	0
1111-11-11	4	1500	1067	Dresden	Sachsen	6500	4	334,23	2759	Name 2759	309930	blubblub	19,89	0,00	0,00	35,88	000000		12 Months	0,00	1	01.12.2011		0,00	0	0	0	0	10.12.2011	1	4	0


----------



## Evil-Devil (30. Jan 2012)

Wieso willst du an der Schleife festhalten? Wenn du dir die Daten gruppiert ausgeben lässt, dann bekommst du effektiv alle Werte die du dir einzeln über die Schleife holen würdest direkt zurück. Du würdest dir dann einige Schleifendurchgänge ersparen.

Was du noch zusätzlich machen könntest, wäre die Daten aus dem Select in eine extra Tabelle zu schreiben und dann über selbige das Update zu steuern. So hättest du zum einen die Ergebnisse der letzten Select Anweisung direkt vorliegen und könntest sie bei Bedarf auslesen oder du führst das Update über ein Subselect aus.

@Tabelle. Die solltest du mal normalisieren oder als reine Import Tabelle nur nutzen


----------



## Bazilinho (31. Jan 2012)

Naja.. 
Ich hatte das so gemacht, weil ich insgesamt 12 Ergebnistabellen habe. 
Diese vv_data Tabelle ist - wie Du mir auch geraten hast - eine reine Import-Tabelle. 

Ich hab noch ein externes Tool, was die Daten über eine ODBC-Verbindung braucht, aber nur mit relativ kleinen Datenmengen umgehen kann.. Daher der Aufwand mit 12 Ergebnistabellen etc. 

Die Ergebnistabellen sind pro vv_02_usage, also 1500, 2000,... angelegt. 
Und in jeder dieser Ergebnistabellen gibt es 10 Spalten (für Rang 1-10). In Abhängigkeit des Ranges soll also in den entsprechenden Spalte der eien String T_00.... geschrieben werden...

Hatte mir Dein Statement mal so geändert, dass ich die Ergebnisse nicht mti Group BY, sondern Ordern BY anzeige. War genau das was mir (mit meinem Verständnis :shock  helfen würde. Lasse ich mir einzelne PLZs anzeige = TOP, will ich alle haben kackt die Workbench immer ab... ???:L


----------

