[{"data":1,"prerenderedAt":1892},["ShallowReactive",2],{"doc:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-million-rows-to-excel-with-xlsxwriter-constant-memory":3,"surround:\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-million-rows-to-excel-with-xlsxwriter-constant-memory":1884},{"id":4,"title":5,"body":6,"dateModified":1852,"datePublished":1852,"description":1853,"extension":1854,"faq":1855,"meta":1865,"navigation":209,"path":1876,"seo":1877,"slug":1880,"stem":1881,"type":1882,"__hash__":1883},"docs\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-million-rows-to-excel-with-xlsxwriter-constant-memory\u002Findex.md","Write a Million Rows to Excel with xlsxwriter",{"type":7,"value":8,"toc":1839},"minimark",[9,22,31,145,150,174,181,185,188,491,501,505,517,595,690,693,716,723,727,730,1125,1128,1132,1139,1395,1407,1489,1493,1496,1575,1585,1597,1601,1720,1724,1727,1738,1742,1750,1754,1763,1772,1781,1790,1794,1797,1804,1807,1835],[10,11,12,13,17,18,21],"p",{},"There is a point where writing an Excel file stops being a formatting problem and becomes a memory problem. In its default mode, xlsxwriter holds every written cell until ",[14,15,16],"code",{},"close()",", which is fast and flexible up to a few hundred thousand rows and then starts consuming gigabytes. ",[14,19,20],{},"constant_memory"," mode changes that: each row is serialised as soon as the next one begins, so peak memory stays flat whether the sheet has ten thousand rows or a million.",[10,23,24,25,30],{},"The mode costs something real, and knowing exactly what makes the difference between using it well and fighting it. This guide, part of ",[26,27,29],"a",{"href":28},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002F","Building Excel Reports with xlsxwriter",", covers the trade, the write-order rules, the format's hard limits, and the question worth asking before any of it.",[32,33,41,42,41,46,41,50,41,57,41,64,41,72,41,75,41,80,41,83,41,88,41,92,41,96,41,100,41,107,41,114,41,120,41,124,41,128,41,133,41,140],"svg",{"viewBox":34,"role":35,"ariaLabelledBy":36,"xmlns":39,"style":40},"0 0 740 250","img",[37,38],"mr-mem-t","mr-mem-d","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","width:100%;max-width:740px;height:auto;display:block;margin:1.5rem auto;font-family:Inter,ui-sans-serif,system-ui,sans-serif","\n  ",[43,44,45],"title",{"id":37},"Memory against rows written, in each mode",[47,48,49],"desc",{"id":38},"In default mode memory rises steadily with the number of rows written, because every cell object is retained until the workbook is closed. In constant memory mode it stays flat at roughly the size of a single row, because each row is flushed to a temporary file as soon as the next one starts.",[51,52],"rect",{"x":53,"y":53,"width":54,"height":55,"fill":56},"0","740","250","#ffffff",[58,59,63],"text",{"x":60,"y":61,"style":62},"370","30","font-size:12.5px;font-weight:600;fill:var(--muted,#5b6780);text-anchor:middle","Peak memory as the row count grows",[65,66],"line",{"x1":67,"y1":68,"x2":67,"y2":69,"stroke":70,"style":71},"80","52","188","var(--line,#cdd5e6)","stroke-width:2px",[65,73],{"x1":67,"y1":69,"x2":74,"y2":69,"stroke":70,"style":71},"676",[58,76,79],{"x":68,"y":77,"style":78},"60","font-size:10.5px;fill:var(--muted,#5b6780);text-anchor:middle","high",[58,81,82],{"x":68,"y":69,"style":78},"low",[58,84,87],{"x":85,"y":86,"style":78},"120","208","10k",[58,89,91],{"x":90,"y":86,"style":78},"280","100k",[58,93,95],{"x":94,"y":86,"style":78},"450","500k",[58,97,99],{"x":98,"y":86,"style":78},"620","1M rows",[101,102],"polyline",{"points":103,"fill":104,"stroke":105,"style":106},"90,182 200,168 300,140 420,102 540,76 650,56","none","var(--brand,#5b5cf0)","stroke-width:2.5px",[108,109],"circle",{"cx":110,"cy":111,"r":112,"fill":113},"650","56","5","#5b5cf0",[58,115,119],{"x":116,"y":117,"style":118},"596","46","font-size:11.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","default mode",[101,121],{"points":122,"fill":104,"stroke":123,"style":106},"90,178 200,177 300,177 420,176 540,176 650,175","var(--teal,#0f9488)",[108,125],{"cx":110,"cy":126,"r":112,"fill":127},"175","#0f766e",[58,129,20],{"x":130,"y":131,"style":132},"586","167","font-size:11.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle",[51,134],{"x":85,"y":135,"width":136,"height":137,"rx":138,"fill":139},"220","500","24","8","#f0f2f5",[58,141,144],{"x":60,"y":142,"style":143},"237","font-size:11px;fill:var(--muted,#5b6780);text-anchor:middle","The flat line is bought with one restriction: rows must be written in order and never revisited",[146,147,149],"h2",{"id":148},"prerequisites","Prerequisites",[151,152,157],"pre",{"className":153,"code":154,"language":155,"meta":156,"style":156},"language-bash shiki shiki-themes github-light github-dark-high-contrast","pip install xlsxwriter\n","bash","",[14,158,159],{"__ignoreMap":156},[160,161,163,167,171],"span",{"class":65,"line":162},1,[160,164,166],{"class":165},"sMTad","pip",[160,168,170],{"class":169},"srMev"," install",[160,172,173],{"class":169}," xlsxwriter\n",[10,175,176,177,180],{},"You also want somewhere with free disk space for the temporary files — constant-memory mode trades RAM for a spooled temporary directory, and a full ",[14,178,179],{},"\u002Ftmp"," is the most common way this fails on a server.",[146,182,184],{"id":183},"step-1-turn-the-mode-on","Step 1: Turn the mode on",[10,186,187],{},"It is a workbook option, set at construction:",[151,189,193],{"className":190,"code":191,"language":192,"meta":156,"style":156},"language-python shiki shiki-themes github-light github-dark-high-contrast","import xlsxwriter\n\nwb = xlsxwriter.Workbook(\"detail.xlsx\", {\n    \"constant_memory\": True,\n    \"tmpdir\": \"\u002Fvar\u002Ftmp\",              # somewhere with room; defaults to the system temp\n    \"default_date_format\": \"yyyy-mm-dd\",\n})\nws = wb.add_worksheet(\"Detail\")\n\nmoney = wb.add_format({\"num_format\": '#,##0.00'})\nws.set_column(\"A:A\", 12)\nws.set_column(\"B:B\", 18)\nws.set_column(\"C:C\", 14, money)        # column formats still work\n\nws.write_row(0, 0, [\"order_id\", \"region\", \"amount\"])\n\nfor i, (oid, region, amount) in enumerate(source_rows(), start=1):\n    ws.write_number(i, 0, oid)\n    ws.write_string(i, 1, region)\n    ws.write_number(i, 2, amount)\n\nwb.close()\n","python",[14,194,195,204,211,229,245,263,276,282,299,304,325,342,357,376,381,412,417,447,458,469,480,485],{"__ignoreMap":156},[160,196,197,201],{"class":65,"line":162},[160,198,200],{"class":199},"s-kum","import",[160,202,173],{"class":203},"skGVy",[160,205,207],{"class":65,"line":206},2,[160,208,210],{"emptyLinePlaceholder":209},true,"\n",[160,212,214,217,220,223,226],{"class":65,"line":213},3,[160,215,216],{"class":203},"wb ",[160,218,219],{"class":199},"=",[160,221,222],{"class":203}," xlsxwriter.Workbook(",[160,224,225],{"class":169},"\"detail.xlsx\"",[160,227,228],{"class":203},", {\n",[160,230,232,235,238,242],{"class":65,"line":231},4,[160,233,234],{"class":169},"    \"constant_memory\"",[160,236,237],{"class":203},": ",[160,239,241],{"class":240},"sP0c6","True",[160,243,244],{"class":203},",\n",[160,246,248,251,253,256,259],{"class":65,"line":247},5,[160,249,250],{"class":169},"    \"tmpdir\"",[160,252,237],{"class":203},[160,254,255],{"class":169},"\"\u002Fvar\u002Ftmp\"",[160,257,258],{"class":203},",              ",[160,260,262],{"class":261},"s-wDw","# somewhere with room; defaults to the system temp\n",[160,264,266,269,271,274],{"class":65,"line":265},6,[160,267,268],{"class":169},"    \"default_date_format\"",[160,270,237],{"class":203},[160,272,273],{"class":169},"\"yyyy-mm-dd\"",[160,275,244],{"class":203},[160,277,279],{"class":65,"line":278},7,[160,280,281],{"class":203},"})\n",[160,283,285,288,290,293,296],{"class":65,"line":284},8,[160,286,287],{"class":203},"ws ",[160,289,219],{"class":199},[160,291,292],{"class":203}," wb.add_worksheet(",[160,294,295],{"class":169},"\"Detail\"",[160,297,298],{"class":203},")\n",[160,300,302],{"class":65,"line":301},9,[160,303,210],{"emptyLinePlaceholder":209},[160,305,307,310,312,315,318,320,323],{"class":65,"line":306},10,[160,308,309],{"class":203},"money ",[160,311,219],{"class":199},[160,313,314],{"class":203}," wb.add_format({",[160,316,317],{"class":169},"\"num_format\"",[160,319,237],{"class":203},[160,321,322],{"class":169},"'#,##0.00'",[160,324,281],{"class":203},[160,326,328,331,334,337,340],{"class":65,"line":327},11,[160,329,330],{"class":203},"ws.set_column(",[160,332,333],{"class":169},"\"A:A\"",[160,335,336],{"class":203},", ",[160,338,339],{"class":240},"12",[160,341,298],{"class":203},[160,343,345,347,350,352,355],{"class":65,"line":344},12,[160,346,330],{"class":203},[160,348,349],{"class":169},"\"B:B\"",[160,351,336],{"class":203},[160,353,354],{"class":240},"18",[160,356,298],{"class":203},[160,358,360,362,365,367,370,373],{"class":65,"line":359},13,[160,361,330],{"class":203},[160,363,364],{"class":169},"\"C:C\"",[160,366,336],{"class":203},[160,368,369],{"class":240},"14",[160,371,372],{"class":203},", money)        ",[160,374,375],{"class":261},"# column formats still work\n",[160,377,379],{"class":65,"line":378},14,[160,380,210],{"emptyLinePlaceholder":209},[160,382,384,387,389,391,393,396,399,401,404,406,409],{"class":65,"line":383},15,[160,385,386],{"class":203},"ws.write_row(",[160,388,53],{"class":240},[160,390,336],{"class":203},[160,392,53],{"class":240},[160,394,395],{"class":203},", [",[160,397,398],{"class":169},"\"order_id\"",[160,400,336],{"class":203},[160,402,403],{"class":169},"\"region\"",[160,405,336],{"class":203},[160,407,408],{"class":169},"\"amount\"",[160,410,411],{"class":203},"])\n",[160,413,415],{"class":65,"line":414},16,[160,416,210],{"emptyLinePlaceholder":209},[160,418,420,423,426,429,432,435,439,441,444],{"class":65,"line":419},17,[160,421,422],{"class":199},"for",[160,424,425],{"class":203}," i, (oid, region, amount) ",[160,427,428],{"class":199},"in",[160,430,431],{"class":240}," enumerate",[160,433,434],{"class":203},"(source_rows(), ",[160,436,438],{"class":437},"sa561","start",[160,440,219],{"class":199},[160,442,443],{"class":240},"1",[160,445,446],{"class":203},"):\n",[160,448,450,453,455],{"class":65,"line":449},18,[160,451,452],{"class":203},"    ws.write_number(i, ",[160,454,53],{"class":240},[160,456,457],{"class":203},", oid)\n",[160,459,461,464,466],{"class":65,"line":460},19,[160,462,463],{"class":203},"    ws.write_string(i, ",[160,465,443],{"class":240},[160,467,468],{"class":203},", region)\n",[160,470,472,474,477],{"class":65,"line":471},20,[160,473,452],{"class":203},[160,475,476],{"class":240},"2",[160,478,479],{"class":203},", amount)\n",[160,481,483],{"class":65,"line":482},21,[160,484,210],{"emptyLinePlaceholder":209},[160,486,488],{"class":65,"line":487},22,[160,489,490],{"class":203},"wb.close()\n",[10,492,493,496,497,500],{},[14,494,495],{},"set_column"," is still available and still cheap, which matters more here than in the default mode: with no ability to revisit cells, column-level formatting is the main styling tool you have left. ",[14,498,499],{},"default_date_format"," is worth setting for the same reason — it applies to every date written without an explicit format, saving you from passing one on every call.",[146,502,504],{"id":503},"step-2-respect-the-write-order","Step 2: Respect the write order",[10,506,507,508,512,513,516],{},"The rule is simple and unforgiving: rows must be written in increasing order, and once you start row ",[509,510,511],"em",{},"n+1",", row ",[509,514,515],{},"n"," is gone.",[151,518,520],{"className":190,"code":519,"language":192,"meta":156,"style":156},"ws.write(0, 0, \"header\")\nws.write(1, 0, \"first\")\nws.write(2, 0, \"second\")\nws.write(1, 1, \"late\")          # silently lost — row 1 was already flushed\n",[14,521,522,540,557,574],{"__ignoreMap":156},[160,523,524,527,529,531,533,535,538],{"class":65,"line":162},[160,525,526],{"class":203},"ws.write(",[160,528,53],{"class":240},[160,530,336],{"class":203},[160,532,53],{"class":240},[160,534,336],{"class":203},[160,536,537],{"class":169},"\"header\"",[160,539,298],{"class":203},[160,541,542,544,546,548,550,552,555],{"class":65,"line":206},[160,543,526],{"class":203},[160,545,443],{"class":240},[160,547,336],{"class":203},[160,549,53],{"class":240},[160,551,336],{"class":203},[160,553,554],{"class":169},"\"first\"",[160,556,298],{"class":203},[160,558,559,561,563,565,567,569,572],{"class":65,"line":213},[160,560,526],{"class":203},[160,562,476],{"class":240},[160,564,336],{"class":203},[160,566,53],{"class":240},[160,568,336],{"class":203},[160,570,571],{"class":169},"\"second\"",[160,573,298],{"class":203},[160,575,576,578,580,582,584,586,589,592],{"class":65,"line":231},[160,577,526],{"class":203},[160,579,443],{"class":240},[160,581,336],{"class":203},[160,583,443],{"class":240},[160,585,336],{"class":203},[160,587,588],{"class":169},"\"late\"",[160,590,591],{"class":203},")          ",[160,593,594],{"class":261},"# silently lost — row 1 was already flushed\n",[32,596,41,601,41,604,41,607,41,610,41,613,41,620,41,625,41,630,41,634,41,638,41,641,41,645,41,648,41,651,41,653,41,657,41,661,41,664,41,667,41,669,41,675,41,680,41,683,41,686],{"viewBox":597,"role":35,"ariaLabelledBy":598,"xmlns":39,"style":40},"0 0 740 216",[599,600],"mr-order-t","mr-order-d",[43,602,603],{"id":599},"Which writes are still allowed once a row has been flushed",[47,605,606],{"id":600},"Writing further cells in the current row is allowed. Starting the next row is allowed and flushes the previous one. Writing to a different worksheet is allowed, because each sheet tracks its own current row. Writing back to a row already flushed is silently discarded — there is no error to catch.",[51,608],{"x":53,"y":53,"width":54,"height":609,"fill":56},"216",[58,611,612],{"x":60,"y":61,"style":62},"The current row is row 500 on sheet \"Detail\"",[51,614],{"x":615,"y":616,"width":617,"height":618,"rx":339,"fill":619,"stroke":123,"style":71},"16","50","170","146","#d9f4f1",[58,621,624],{"x":622,"y":623,"style":132},"101","78","more of row 500",[58,626,629],{"x":622,"y":627,"style":628},"108","font-size:11px;fill:var(--text,#172033);text-anchor:middle","any column,",[58,631,633],{"x":622,"y":632,"style":628},"128","any order",[58,635,637],{"x":622,"y":636,"style":143},"164","allowed",[51,639],{"x":640,"y":616,"width":617,"height":618,"rx":339,"fill":619,"stroke":123,"style":71},"198",[58,642,644],{"x":643,"y":623,"style":132},"283","row 501",[58,646,647],{"x":643,"y":627,"style":628},"flushes row 500",[58,649,650],{"x":643,"y":632,"style":628},"to the spool",[58,652,637],{"x":643,"y":636,"style":143},[51,654],{"x":655,"y":616,"width":617,"height":618,"rx":339,"fill":656,"stroke":105,"style":71},"380","#ebebfd",[58,658,660],{"x":659,"y":623,"style":118},"465","sheet \"Summary\"",[58,662,663],{"x":659,"y":627,"style":628},"its own current",[58,665,666],{"x":659,"y":632,"style":628},"row, unaffected",[58,668,637],{"x":659,"y":636,"style":143},[51,670],{"x":671,"y":616,"width":672,"height":618,"rx":339,"fill":673,"stroke":674,"style":71},"562","162","#fce9e9","var(--accent-ink,#be185d)",[58,676,679],{"x":677,"y":623,"style":678},"643","font-size:11.5px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","row 499",[58,681,682],{"x":677,"y":627,"style":628},"already written",[58,684,685],{"x":677,"y":632,"style":628},"out and freed",[58,687,689],{"x":677,"y":636,"style":688},"font-size:11px;font-weight:700;fill:var(--accent-ink,#be185d);text-anchor:middle","silently discarded",[10,691,692],{},"There is no exception and no warning. In practice this rules out three habits that are normal in the default mode:",[694,695,696,704,710],"ul",{},[697,698,699,703],"li",{},[700,701,702],"strong",{},"A total row at the top."," Compute it first from the source data, or put it on a separate summary sheet written before the detail sheet.",[697,705,706,709],{},[700,707,708],{},"Measured column widths."," You cannot look at the data and then size the columns, because by the time you have seen it the rows are written. Size from the schema — a known-length ID, a currency column — or take one pass over a sample to estimate.",[697,711,712,715],{},[700,713,714],{},"Post-hoc highlighting."," Decide the format as each row is written, from the values you already have in hand.",[10,717,718,719,722],{},"Within a row, cells can be written in any column order; it is only the row index that must not go backwards. Writing to a ",[509,720,721],{},"different"," sheet is fine at any time — each worksheet keeps its own current row — which is exactly what makes the summary-sheet pattern work.",[146,724,726],{"id":725},"step-3-put-the-totals-somewhere-they-can-exist","Step 3: Put the totals somewhere they can exist",[10,728,729],{},"Because the detail sheet cannot be revisited, accumulate as you stream and write the summary afterwards on its own sheet:",[151,731,733],{"className":190,"code":732,"language":192,"meta":156,"style":156},"from collections import defaultdict\n\nwb = xlsxwriter.Workbook(\"detail.xlsx\", {\"constant_memory\": True})\ndetail = wb.add_worksheet(\"Detail\")\nsummary = wb.add_worksheet(\"Summary\")          # created now, written later\n\nbold = wb.add_format({\"bold\": True})\nmoney = wb.add_format({\"num_format\": '#,##0.00'})\n\ndetail.write_row(0, 0, [\"order_id\", \"region\", \"amount\"], bold)\ndetail.set_column(\"C:C\", 14, money)\n\ntotals, count = defaultdict(float), 0\nfor i, (oid, region, amount) in enumerate(source_rows(), start=1):\n    detail.write_number(i, 0, oid)\n    detail.write_string(i, 1, region)\n    detail.write_number(i, 2, amount)\n    totals[region] += amount\n    count += 1\n\nsummary.write_row(0, 0, [\"Region\", \"Amount\"], bold)\nsummary.set_column(\"B:B\", 16, money)\nfor r, (region, total) in enumerate(sorted(totals.items()), start=1):\n    summary.write_string(r, 0, region)\n    summary.write_number(r, 1, total)\nsummary.write_string(len(totals) + 2, 0, f\"{count:,} detail rows\", bold)\n\nwb.close()\n",[14,734,735,748,752,774,787,804,808,826,842,846,872,886,890,909,929,938,947,955,966,976,980,1003,1016,1045,1055,1066,1115,1120],{"__ignoreMap":156},[160,736,737,740,743,745],{"class":65,"line":162},[160,738,739],{"class":199},"from",[160,741,742],{"class":203}," collections ",[160,744,200],{"class":199},[160,746,747],{"class":203}," defaultdict\n",[160,749,750],{"class":65,"line":206},[160,751,210],{"emptyLinePlaceholder":209},[160,753,754,756,758,760,762,765,768,770,772],{"class":65,"line":213},[160,755,216],{"class":203},[160,757,219],{"class":199},[160,759,222],{"class":203},[160,761,225],{"class":169},[160,763,764],{"class":203},", {",[160,766,767],{"class":169},"\"constant_memory\"",[160,769,237],{"class":203},[160,771,241],{"class":240},[160,773,281],{"class":203},[160,775,776,779,781,783,785],{"class":65,"line":231},[160,777,778],{"class":203},"detail ",[160,780,219],{"class":199},[160,782,292],{"class":203},[160,784,295],{"class":169},[160,786,298],{"class":203},[160,788,789,792,794,796,799,801],{"class":65,"line":247},[160,790,791],{"class":203},"summary ",[160,793,219],{"class":199},[160,795,292],{"class":203},[160,797,798],{"class":169},"\"Summary\"",[160,800,591],{"class":203},[160,802,803],{"class":261},"# created now, written later\n",[160,805,806],{"class":65,"line":265},[160,807,210],{"emptyLinePlaceholder":209},[160,809,810,813,815,817,820,822,824],{"class":65,"line":278},[160,811,812],{"class":203},"bold ",[160,814,219],{"class":199},[160,816,314],{"class":203},[160,818,819],{"class":169},"\"bold\"",[160,821,237],{"class":203},[160,823,241],{"class":240},[160,825,281],{"class":203},[160,827,828,830,832,834,836,838,840],{"class":65,"line":284},[160,829,309],{"class":203},[160,831,219],{"class":199},[160,833,314],{"class":203},[160,835,317],{"class":169},[160,837,237],{"class":203},[160,839,322],{"class":169},[160,841,281],{"class":203},[160,843,844],{"class":65,"line":301},[160,845,210],{"emptyLinePlaceholder":209},[160,847,848,851,853,855,857,859,861,863,865,867,869],{"class":65,"line":306},[160,849,850],{"class":203},"detail.write_row(",[160,852,53],{"class":240},[160,854,336],{"class":203},[160,856,53],{"class":240},[160,858,395],{"class":203},[160,860,398],{"class":169},[160,862,336],{"class":203},[160,864,403],{"class":169},[160,866,336],{"class":203},[160,868,408],{"class":169},[160,870,871],{"class":203},"], bold)\n",[160,873,874,877,879,881,883],{"class":65,"line":327},[160,875,876],{"class":203},"detail.set_column(",[160,878,364],{"class":169},[160,880,336],{"class":203},[160,882,369],{"class":240},[160,884,885],{"class":203},", money)\n",[160,887,888],{"class":65,"line":344},[160,889,210],{"emptyLinePlaceholder":209},[160,891,892,895,897,900,903,906],{"class":65,"line":359},[160,893,894],{"class":203},"totals, count ",[160,896,219],{"class":199},[160,898,899],{"class":203}," defaultdict(",[160,901,902],{"class":240},"float",[160,904,905],{"class":203},"), ",[160,907,908],{"class":240},"0\n",[160,910,911,913,915,917,919,921,923,925,927],{"class":65,"line":378},[160,912,422],{"class":199},[160,914,425],{"class":203},[160,916,428],{"class":199},[160,918,431],{"class":240},[160,920,434],{"class":203},[160,922,438],{"class":437},[160,924,219],{"class":199},[160,926,443],{"class":240},[160,928,446],{"class":203},[160,930,931,934,936],{"class":65,"line":383},[160,932,933],{"class":203},"    detail.write_number(i, ",[160,935,53],{"class":240},[160,937,457],{"class":203},[160,939,940,943,945],{"class":65,"line":414},[160,941,942],{"class":203},"    detail.write_string(i, ",[160,944,443],{"class":240},[160,946,468],{"class":203},[160,948,949,951,953],{"class":65,"line":419},[160,950,933],{"class":203},[160,952,476],{"class":240},[160,954,479],{"class":203},[160,956,957,960,963],{"class":65,"line":449},[160,958,959],{"class":203},"    totals[region] ",[160,961,962],{"class":199},"+=",[160,964,965],{"class":203}," amount\n",[160,967,968,971,973],{"class":65,"line":460},[160,969,970],{"class":203},"    count ",[160,972,962],{"class":199},[160,974,975],{"class":240}," 1\n",[160,977,978],{"class":65,"line":471},[160,979,210],{"emptyLinePlaceholder":209},[160,981,982,985,987,989,991,993,996,998,1001],{"class":65,"line":482},[160,983,984],{"class":203},"summary.write_row(",[160,986,53],{"class":240},[160,988,336],{"class":203},[160,990,53],{"class":240},[160,992,395],{"class":203},[160,994,995],{"class":169},"\"Region\"",[160,997,336],{"class":203},[160,999,1000],{"class":169},"\"Amount\"",[160,1002,871],{"class":203},[160,1004,1005,1008,1010,1012,1014],{"class":65,"line":487},[160,1006,1007],{"class":203},"summary.set_column(",[160,1009,349],{"class":169},[160,1011,336],{"class":203},[160,1013,615],{"class":240},[160,1015,885],{"class":203},[160,1017,1019,1021,1024,1026,1028,1031,1034,1037,1039,1041,1043],{"class":65,"line":1018},23,[160,1020,422],{"class":199},[160,1022,1023],{"class":203}," r, (region, total) ",[160,1025,428],{"class":199},[160,1027,431],{"class":240},[160,1029,1030],{"class":203},"(",[160,1032,1033],{"class":240},"sorted",[160,1035,1036],{"class":203},"(totals.items()), ",[160,1038,438],{"class":437},[160,1040,219],{"class":199},[160,1042,443],{"class":240},[160,1044,446],{"class":203},[160,1046,1048,1051,1053],{"class":65,"line":1047},24,[160,1049,1050],{"class":203},"    summary.write_string(r, ",[160,1052,53],{"class":240},[160,1054,468],{"class":203},[160,1056,1058,1061,1063],{"class":65,"line":1057},25,[160,1059,1060],{"class":203},"    summary.write_number(r, ",[160,1062,443],{"class":240},[160,1064,1065],{"class":203},", total)\n",[160,1067,1069,1072,1075,1078,1081,1084,1086,1088,1090,1093,1096,1100,1103,1106,1109,1112],{"class":65,"line":1068},26,[160,1070,1071],{"class":203},"summary.write_string(",[160,1073,1074],{"class":240},"len",[160,1076,1077],{"class":203},"(totals) ",[160,1079,1080],{"class":199},"+",[160,1082,1083],{"class":240}," 2",[160,1085,336],{"class":203},[160,1087,53],{"class":240},[160,1089,336],{"class":203},[160,1091,1092],{"class":199},"f",[160,1094,1095],{"class":169},"\"",[160,1097,1099],{"class":1098},"sSjpA","{",[160,1101,1102],{"class":203},"count",[160,1104,1105],{"class":199},":,",[160,1107,1108],{"class":1098},"}",[160,1110,1111],{"class":169}," detail rows\"",[160,1113,1114],{"class":203},", bold)\n",[160,1116,1118],{"class":65,"line":1117},27,[160,1119,210],{"emptyLinePlaceholder":209},[160,1121,1123],{"class":65,"line":1122},28,[160,1124,490],{"class":203},[10,1126,1127],{},"A dictionary of running totals costs a few kilobytes regardless of how many rows pass through it, so the summary is effectively free. This is also better reporting than a total row on a million-row sheet, which nobody will ever scroll to.",[146,1129,1131],{"id":1130},"step-4-handle-the-formats-hard-limits","Step 4: Handle the format's hard limits",[10,1133,1134,1135,1138],{},"A worksheet holds at most 1,048,576 rows and 16,384 columns. That is the ",[14,1136,1137],{},".xlsx"," specification, not a library restriction, so the only answer is to split:",[151,1140,1142],{"className":190,"code":1141,"language":192,"meta":156,"style":156},"ROWS_PER_SHEET = 1_000_000          # leave headroom below the 1,048,576 limit\n\n\ndef write_split(rows, path, headers):\n    wb = xlsxwriter.Workbook(path, {\"constant_memory\": True})\n    bold = wb.add_format({\"bold\": True})\n    ws, sheet_no, row_no = None, 0, 0\n\n    for row in rows:\n        if ws is None or row_no > ROWS_PER_SHEET:\n            sheet_no += 1\n            ws = wb.add_worksheet(f\"Detail {sheet_no}\")\n            ws.write_row(0, 0, headers, bold)\n            ws.set_column(0, len(headers) - 1, 16)\n            row_no = 1\n        ws.write_row(row_no, 0, row)\n        row_no += 1\n\n    wb.close()\n    return sheet_no\n",[14,1143,1144,1158,1162,1166,1178,1196,1213,1231,1235,1248,1276,1285,1310,1324,1350,1359,1369,1378,1382,1387],{"__ignoreMap":156},[160,1145,1146,1149,1152,1155],{"class":65,"line":162},[160,1147,1148],{"class":240},"ROWS_PER_SHEET",[160,1150,1151],{"class":199}," =",[160,1153,1154],{"class":240}," 1_000_000",[160,1156,1157],{"class":261},"          # leave headroom below the 1,048,576 limit\n",[160,1159,1160],{"class":65,"line":206},[160,1161,210],{"emptyLinePlaceholder":209},[160,1163,1164],{"class":65,"line":213},[160,1165,210],{"emptyLinePlaceholder":209},[160,1167,1168,1171,1175],{"class":65,"line":231},[160,1169,1170],{"class":199},"def",[160,1172,1174],{"class":1173},"s_Opv"," write_split",[160,1176,1177],{"class":203},"(rows, path, headers):\n",[160,1179,1180,1183,1185,1188,1190,1192,1194],{"class":65,"line":247},[160,1181,1182],{"class":203},"    wb ",[160,1184,219],{"class":199},[160,1186,1187],{"class":203}," xlsxwriter.Workbook(path, {",[160,1189,767],{"class":169},[160,1191,237],{"class":203},[160,1193,241],{"class":240},[160,1195,281],{"class":203},[160,1197,1198,1201,1203,1205,1207,1209,1211],{"class":65,"line":265},[160,1199,1200],{"class":203},"    bold ",[160,1202,219],{"class":199},[160,1204,314],{"class":203},[160,1206,819],{"class":169},[160,1208,237],{"class":203},[160,1210,241],{"class":240},[160,1212,281],{"class":203},[160,1214,1215,1218,1220,1223,1225,1227,1229],{"class":65,"line":278},[160,1216,1217],{"class":203},"    ws, sheet_no, row_no ",[160,1219,219],{"class":199},[160,1221,1222],{"class":240}," None",[160,1224,336],{"class":203},[160,1226,53],{"class":240},[160,1228,336],{"class":203},[160,1230,908],{"class":240},[160,1232,1233],{"class":65,"line":284},[160,1234,210],{"emptyLinePlaceholder":209},[160,1236,1237,1240,1243,1245],{"class":65,"line":301},[160,1238,1239],{"class":199},"    for",[160,1241,1242],{"class":203}," row ",[160,1244,428],{"class":199},[160,1246,1247],{"class":203}," rows:\n",[160,1249,1250,1253,1256,1259,1261,1264,1267,1270,1273],{"class":65,"line":306},[160,1251,1252],{"class":199},"        if",[160,1254,1255],{"class":203}," ws ",[160,1257,1258],{"class":199},"is",[160,1260,1222],{"class":240},[160,1262,1263],{"class":199}," or",[160,1265,1266],{"class":203}," row_no ",[160,1268,1269],{"class":199},">",[160,1271,1272],{"class":240}," ROWS_PER_SHEET",[160,1274,1275],{"class":203},":\n",[160,1277,1278,1281,1283],{"class":65,"line":327},[160,1279,1280],{"class":203},"            sheet_no ",[160,1282,962],{"class":199},[160,1284,975],{"class":240},[160,1286,1287,1290,1292,1294,1296,1299,1301,1304,1306,1308],{"class":65,"line":344},[160,1288,1289],{"class":203},"            ws ",[160,1291,219],{"class":199},[160,1293,292],{"class":203},[160,1295,1092],{"class":199},[160,1297,1298],{"class":169},"\"Detail ",[160,1300,1099],{"class":1098},[160,1302,1303],{"class":203},"sheet_no",[160,1305,1108],{"class":1098},[160,1307,1095],{"class":169},[160,1309,298],{"class":203},[160,1311,1312,1315,1317,1319,1321],{"class":65,"line":359},[160,1313,1314],{"class":203},"            ws.write_row(",[160,1316,53],{"class":240},[160,1318,336],{"class":203},[160,1320,53],{"class":240},[160,1322,1323],{"class":203},", headers, bold)\n",[160,1325,1326,1329,1331,1333,1335,1338,1341,1344,1346,1348],{"class":65,"line":378},[160,1327,1328],{"class":203},"            ws.set_column(",[160,1330,53],{"class":240},[160,1332,336],{"class":203},[160,1334,1074],{"class":240},[160,1336,1337],{"class":203},"(headers) ",[160,1339,1340],{"class":199},"-",[160,1342,1343],{"class":240}," 1",[160,1345,336],{"class":203},[160,1347,615],{"class":240},[160,1349,298],{"class":203},[160,1351,1352,1355,1357],{"class":65,"line":383},[160,1353,1354],{"class":203},"            row_no ",[160,1356,219],{"class":199},[160,1358,975],{"class":240},[160,1360,1361,1364,1366],{"class":65,"line":414},[160,1362,1363],{"class":203},"        ws.write_row(row_no, ",[160,1365,53],{"class":240},[160,1367,1368],{"class":203},", row)\n",[160,1370,1371,1374,1376],{"class":65,"line":419},[160,1372,1373],{"class":203},"        row_no ",[160,1375,962],{"class":199},[160,1377,975],{"class":240},[160,1379,1380],{"class":65,"line":449},[160,1381,210],{"emptyLinePlaceholder":209},[160,1383,1384],{"class":65,"line":460},[160,1385,1386],{"class":203},"    wb.close()\n",[160,1388,1389,1392],{"class":65,"line":471},[160,1390,1391],{"class":199},"    return",[160,1393,1394],{"class":203}," sheet_no\n",[10,1396,1397,1398,1401,1402,1406],{},"Splitting across sheets keeps everything in one file, which is convenient but produces a workbook that takes a long time to open. Splitting across ",[509,1399,1400],{},"files"," — one per region, per month, per whatever the reader actually filters by — is nearly always the better experience, and it is what ",[26,1403,1405],{"href":1404},"\u002Fautomating-reporting-workflows\u002Fgenerating-excel-reports-from-templates\u002Fgenerate-one-excel-report-per-region-in-a-loop\u002F","Generate One Excel Report per Region in a Loop"," covers.",[32,1408,41,1413,41,1416,41,1419,41,1422,41,1425,41,1430,41,1435,41,1440,41,1443,41,1446,41,1450,41,1453,41,1457,41,1460,41,1463,41,1466,41,1469,41,1472,41,1477,41,1480,41,1483,41,1486],{"viewBox":1409,"role":35,"ariaLabelledBy":1410,"xmlns":39,"style":40},"0 0 740 244",[1411,1412],"mr-limit-t","mr-limit-d",[43,1414,1415],{"id":1411},"Three ways to handle more rows than one sheet can hold",[47,1417,1418],{"id":1412},"Splitting across sheets keeps one file but makes it slow to open. Splitting across files by the dimension readers filter on gives each person a workbook they can actually use. Writing a summary workbook plus a CSV or Parquet detail file is the option that stays fast at any size.",[51,1420],{"x":53,"y":53,"width":54,"height":1421,"fill":56},"244",[58,1423,1424],{"x":60,"y":61,"style":62},"Past about a million rows, the question is who opens it and how",[51,1426],{"x":615,"y":616,"width":1427,"height":636,"rx":369,"fill":1428,"stroke":1429,"style":71},"228","#fdefd8","var(--gold,#b4740a)",[58,1431,1434],{"x":1432,"y":623,"style":1433},"130","font-size:12.5px;font-weight:700;fill:var(--gold-ink,#7a4e06);text-anchor:middle","split across sheets",[58,1436,1439],{"x":1432,"y":1437,"style":1438},"106","font-size:11.5px;fill:var(--text,#172033);text-anchor:middle","one file, Detail 1..n",[58,1441,1442],{"x":1432,"y":1432,"style":1438},"nothing to reassemble",[58,1444,1445],{"x":1432,"y":672,"style":143},"slow to open, and a filter",[58,1447,1449],{"x":1432,"y":1448,"style":143},"180","only sees one sheet at a time",[51,1451],{"x":1452,"y":616,"width":1427,"height":636,"rx":369,"fill":656,"stroke":105,"style":71},"256",[58,1454,1456],{"x":60,"y":623,"style":1455},"font-size:12.5px;font-weight:700;fill:var(--brand-strong,#4338ca);text-anchor:middle","split across files",[58,1458,1459],{"x":60,"y":1437,"style":1438},"one per region or month",[58,1461,1462],{"x":60,"y":1432,"style":1438},"each opens instantly",[58,1464,1465],{"x":60,"y":672,"style":143},"matches how people already",[58,1467,1468],{"x":60,"y":1448,"style":143},"ask for the data",[51,1470],{"x":1471,"y":616,"width":1427,"height":636,"rx":369,"fill":619,"stroke":123,"style":71},"496",[58,1473,1476],{"x":1474,"y":623,"style":1475},"610","font-size:12.5px;font-weight:700;fill:var(--teal-ink,#0b6157);text-anchor:middle","summary + flat file",[58,1478,1479],{"x":1474,"y":1437,"style":1438},"small .xlsx to read",[58,1481,1482],{"x":1474,"y":1432,"style":1438},"CSV or Parquet detail",[58,1484,1485],{"x":1474,"y":672,"style":143},"stays fast at any size —",[58,1487,1488],{"x":1474,"y":1448,"style":143},"the usual right answer",[146,1490,1492],{"id":1491},"step-5-know-where-the-time-goes","Step 5: Know where the time goes",[10,1494,1495],{},"Constant-memory mode fixes memory, not speed. At a million rows the cost breaks down roughly like this:",[1497,1498,1499,1515],"table",{},[1500,1501,1502],"thead",{},[1503,1504,1505,1509,1512],"tr",{},[1506,1507,1508],"th",{},"Stage",[1506,1510,1511],{},"What dominates",[1506,1513,1514],{},"What helps",[1516,1517,1518,1530,1547,1562],"tbody",{},[1503,1519,1520,1524,1527],{},[1521,1522,1523],"td",{},"Producing the rows",[1521,1525,1526],{},"The query or the source read",[1521,1528,1529],{},"Stream from the database; do not build a DataFrame first",[1503,1531,1532,1538,1541],{},[1521,1533,1534,1537],{},[14,1535,1536],{},"write_*"," calls",[1521,1539,1540],{},"Python-level per-cell work",[1521,1542,1543,1546],{},[14,1544,1545],{},"write_row"," over per-cell calls; avoid per-cell formats",[1503,1548,1549,1552,1555],{},[1521,1550,1551],{},"Serialising",[1521,1553,1554],{},"XML generation and the temp spool",[1521,1556,1557,1558,1561],{},"A fast ",[14,1559,1560],{},"tmpdir","; fewer columns",[1503,1563,1564,1569,1572],{},[1521,1565,1566,1567],{},"Zipping at ",[14,1568,16],{},[1521,1570,1571],{},"Compression of the sheet XML",[1521,1573,1574],{},"Unavoidable; it is a large share of the total",[10,1576,1577,1578,1580,1581,1584],{},"The practical lever is columns, not rows: dropping four unused columns from a million-row export removes four million cells and takes a proportional slice off every stage. The second lever is ",[14,1579,1545],{}," over three separate ",[14,1582,1583],{},"write_number"," calls, which cuts the Python-side overhead noticeably at this scale.",[10,1586,1587,1588,1592,1593,1596],{},"If the source is a database, stream it rather than materialising it — ",[26,1589,1591],{"href":1590},"\u002Fadvanced-data-transformation-and-cleaning\u002Fmoving-data-between-excel-and-databases\u002Fexport-sql-query-results-to-excel-with-python\u002F","Export SQL Query Results to Excel with Python"," covers ",[14,1594,1595],{},"stream_results"," and chunked reads, and the two techniques compose: a chunked read feeding a constant-memory write holds neither side in memory.",[146,1598,1600],{"id":1599},"common-pitfalls-and-gotchas","Common pitfalls and gotchas",[1497,1602,1603,1616],{},[1500,1604,1605],{},[1503,1606,1607,1610,1613],{},[1506,1608,1609],{},"Symptom",[1506,1611,1612],{},"Cause",[1506,1614,1615],{},"Fix",[1516,1617,1618,1629,1642,1661,1674,1685,1698,1709],{},[1503,1619,1620,1623,1626],{},[1521,1621,1622],{},"Cells silently missing",[1521,1624,1625],{},"A row written after moving past it",[1521,1627,1628],{},"Write strictly top to bottom",[1503,1630,1631,1636,1639],{},[1521,1632,1633],{},[14,1634,1635],{},"Worksheet row or column index out of range",[1521,1637,1638],{},"Past 1,048,576 rows or 16,384 columns",[1521,1640,1641],{},"Split sheets or files",[1503,1643,1644,1652,1655],{},[1521,1645,1646,1649,1650],{},[14,1647,1648],{},"No space left on device"," at ",[14,1651,16],{},[1521,1653,1654],{},"Temp spool filled the disk",[1521,1656,1657,1658,1660],{},"Set ",[14,1659,1560],{}," to a large volume",[1503,1662,1663,1666,1671],{},[1521,1664,1665],{},"Memory still grows",[1521,1667,1668,1670],{},[14,1669,20],{}," not actually set",[1521,1672,1673],{},"Pass it in the options dict at construction",[1503,1675,1676,1679,1682],{},[1521,1677,1678],{},"Column widths look wrong",[1521,1680,1681],{},"Cannot measure after writing",[1521,1683,1684],{},"Size from the schema, or sample first",[1503,1686,1687,1690,1695],{},[1521,1688,1689],{},"The file will not open",[1521,1691,1692,1694],{},[14,1693,16],{}," never ran",[1521,1696,1697],{},"Use the workbook as a context manager",[1503,1699,1700,1703,1706],{},[1521,1701,1702],{},"Excel takes minutes to open it",[1521,1704,1705],{},"The row count itself",[1521,1707,1708],{},"Ship a summary plus a flat file",[1503,1710,1711,1714,1717],{},[1521,1712,1713],{},"Totals missing from the detail sheet",[1521,1715,1716],{},"Cannot revisit the top",[1521,1718,1719],{},"Accumulate while streaming, write a summary sheet",[146,1721,1723],{"id":1722},"performance-and-scale-notes","Performance and scale notes",[10,1725,1726],{},"As a rule of thumb on ordinary hardware, a million rows of three or four simple columns writes in the low tens of seconds with flat memory in the tens of megabytes; the same write in the default mode is faster per row but climbs into gigabytes and eventually swaps, at which point it is slower by a wide margin. The crossover where constant memory starts winning is usually somewhere between 200,000 and 500,000 rows, depending on column count and how much formatting is attached.",[10,1728,1729,1730,1732,1733,1737],{},"The honest scale note, though, is about the reader. A one-million-row ",[14,1731,1137],{}," takes a long time to open, makes filtering sluggish and cannot be emailed. Before optimising the write, check whether anyone opens the detail at all — ",[26,1734,1736],{"href":1735},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fconvert-excel-to-csv-with-python\u002F","Convert Excel to CSV with Python"," covers the flat-file alternative, and a summary workbook alongside it is almost always what people actually wanted.",[146,1739,1741],{"id":1740},"conclusion","Conclusion",[10,1743,1744,1746,1747,1749],{},[14,1745,20],{}," makes a workbook of any size writable in flat memory, at the cost of random access: rows go out in order and never come back. Design around that — column formats instead of per-cell styling, totals accumulated while streaming and written to a summary sheet, widths chosen from the schema — and give the spool a ",[14,1748,1560],{}," with room. Then step back and check the file is one anybody can open, because splitting by the dimension people filter on, or shipping a summary plus a flat file, usually beats the million-row sheet you were about to generate.",[146,1751,1753],{"id":1752},"frequently-asked-questions","Frequently asked questions",[10,1755,1756,1759,1760,1762],{},[700,1757,1758],{},"What is the row limit for one sheet?","\n1,048,576 rows and 16,384 columns — a hard limit of the ",[14,1761,1137],{}," format, not of any library. Past that you must split across sheets or files.",[10,1764,1765,1771],{},[700,1766,1767,1768,1770],{},"What exactly does ",[14,1769,20],{}," give up?","\nRandom access. Each row is serialised when the next one starts, so rows must be written top to bottom and a written row cannot be revisited — no late totals, no measured column widths, no going back.",[10,1773,1774,1777,1778,1780],{},[700,1775,1776],{},"Can I still format cells in constant memory mode?","\nYes, at write time. Column formats via ",[14,1779,495],{}," and per-cell formats passed to the write call both work; what you cannot do is restyle a row after moving past it.",[10,1782,1783,1786,1787,1789],{},[700,1784,1785],{},"Should I be writing a million rows to Excel at all?","\nUsually not. A workbook that large is slow to open and unusable to filter. Ship a summary workbook plus a CSV or Parquet file for the detail, and reserve the giant sheet for cases where a tool downstream genuinely requires ",[14,1788,1137],{},".",[146,1791,1793],{"id":1792},"related","Related",[10,1795,1796],{},"Up to the parent guide:",[694,1798,1799],{},[697,1800,1801,1803],{},[26,1802,29],{"href":28}," — the write-once model this mode makes stricter.",[10,1805,1806],{},"Related guides:",[694,1808,1809,1816,1823,1828],{},[697,1810,1811,1815],{},[26,1812,1814],{"href":1813},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fwrite-large-dataframes-to-excel-with-write-only-mode\u002F","Write Large DataFrames to Excel with Write-Only Mode"," — openpyxl's equivalent streaming write.",[697,1817,1818,1822],{},[26,1819,1821],{"href":1820},"\u002Fadvanced-data-transformation-and-cleaning\u002Fworking-with-large-excel-files-in-python\u002Fread-large-excel-file-in-chunks-with-pandas\u002F","Read Large Excel Files in Chunks with pandas"," — the reading half of the same problem.",[697,1824,1825,1827],{},[26,1826,1736],{"href":1735}," — the format that stays fast when the row count does not stop growing.",[697,1829,1830,1834],{},[26,1831,1833],{"href":1832},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-formatted-excel-report-with-xlsxwriter\u002F","Write a Formatted Excel Report with xlsxwriter"," — the styling that is still available at this scale.",[1836,1837,1838],"style",{},"html pre.shiki code .sMTad, html code.shiki .sMTad{--shiki-default:#6F42C1;--shiki-dark:#FFB757}html pre.shiki code .srMev, html code.shiki .srMev{--shiki-default:#032F62;--shiki-dark:#ADDCFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-kum, html code.shiki .s-kum{--shiki-default:#D73A49;--shiki-dark:#FF9492}html pre.shiki code .skGVy, html code.shiki .skGVy{--shiki-default:#24292E;--shiki-dark:#F0F3F6}html pre.shiki code .sP0c6, html code.shiki .sP0c6{--shiki-default:#005CC5;--shiki-dark:#91CBFF}html pre.shiki code .s-wDw, html code.shiki .s-wDw{--shiki-default:#6A737D;--shiki-dark:#BDC4CC}html pre.shiki code .sa561, html code.shiki .sa561{--shiki-default:#E36209;--shiki-dark:#FFB757}html pre.shiki code .sSjpA, html code.shiki .sSjpA{--shiki-default:#005CC5;--shiki-dark:#FF9492}html pre.shiki code .s_Opv, html code.shiki .s_Opv{--shiki-default:#6F42C1;--shiki-dark:#DBB7FF}",{"title":156,"searchDepth":206,"depth":206,"links":1840},[1841,1842,1843,1844,1845,1846,1847,1848,1849,1850,1851],{"id":148,"depth":206,"text":149},{"id":183,"depth":206,"text":184},{"id":503,"depth":206,"text":504},{"id":725,"depth":206,"text":726},{"id":1130,"depth":206,"text":1131},{"id":1491,"depth":206,"text":1492},{"id":1599,"depth":206,"text":1600},{"id":1722,"depth":206,"text":1723},{"id":1740,"depth":206,"text":1741},{"id":1752,"depth":206,"text":1753},{"id":1792,"depth":206,"text":1793},"2026-08-10","Constant memory mode explained: what it costs, why rows must be written in order, where the temporary files go, how to split past Excel's 1,048,576-row limit, and when a CSV is the honest answer.","md",[1856,1858,1861,1863],{"q":1758,"a":1857},"1,048,576 rows and 16,384 columns — a hard limit of the .xlsx format, not of any library. Past that you must split across sheets or files.",{"q":1859,"a":1860},"What exactly does constant_memory give up?","Random access. Each row is serialised when the next one starts, so rows must be written top to bottom and a written row cannot be revisited — no late totals, no measured column widths, no going back.",{"q":1776,"a":1862},"Yes, at write time. Column formats via set_column and per-cell formats passed to the write call both work; what you cannot do is restyle a row after moving past it.",{"q":1785,"a":1864},"Usually not. A workbook that large is slow to open and unusable to filter. Ship a summary workbook plus a CSV or Parquet file for the detail, and reserve the giant sheet for cases where a tool downstream genuinely requires .xlsx.",{"breadcrumb":1866},[1867,1870,1873,1874],{"name":1868,"item":1869},"Home","\u002F",{"name":1871,"item":1872},"Formatting and Charting Excel Reports with Python","\u002Fformatting-and-charting-excel-reports-with-python\u002F",{"name":29,"item":28},{"name":5,"item":1875},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-million-rows-to-excel-with-xlsxwriter-constant-memory\u002F","\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-million-rows-to-excel-with-xlsxwriter-constant-memory",{"title":1878,"description":1879},"Write a Million Rows to Excel (xlsxwriter constant memory)","Use xlsxwriter's constant_memory mode to write huge .xlsx files with flat memory — write order rules, tmpdir, sheet splitting past 1,048,576 rows, timing and when to export CSV instead.","write-a-million-rows-to-excel-with-xlsxwriter-constant-memory","formatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-million-rows-to-excel-with-xlsxwriter-constant-memory\u002Findex","how-to","02rdCVgDQb8718IaJfYGKdroOY32Jf_sFSTEuIDJibU",[1885,1888],{"title":1833,"path":1886,"stem":1887,"children":-1},"\u002Fformatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-formatted-excel-report-with-xlsxwriter","formatting-and-charting-excel-reports-with-python\u002Fbuilding-excel-reports-with-xlsxwriter\u002Fwrite-a-formatted-excel-report-with-xlsxwriter\u002Findex",{"title":1889,"path":1890,"stem":1891,"children":-1},"Creating Charts in Excel with openpyxl","\u002Fformatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl","formatting-and-charting-excel-reports-with-python\u002Fcreating-charts-in-excel-with-openpyxl\u002Findex",1786800027370]