The clip tells you to port null ordering with `nulls_first=True`; Polars has no such argument, and its real one, `nulls_last`, already defaults to the behavior being described
Watch on TikTok
A 75.477007-second vertical clip, 576x1024 at an aspect ratio of 0.56, SDR, in a QuickTime/MOV container (mov,mp4,m4a,3gp,3g2,mj2) with an .mp4 extension, carrying H.264 High profile at level 3.1 in yuv420p at exactly 29.97 fps (2997/100, 2,262 video frames over 75.475475 s) at 808,370 bps, plus HE-AACv2 audio at 44,100 Hz stereo at 32,073 bps across 1,629 audio frames, for an overall 847,154 bps and a filesize of 7,992,582 bytes (7.62 MiB); yt-dlp recorded the format as h264_540p_847142-1 and rounded the duration to 75 s with a duration_string of "1:15", and ffprobe confirms every one of those figures against the file rather than the sidecar. It was posted at epoch 1792256400, which is 2026-10-17 17:00:00 UTC, by uploader realpython.com (channel realpython, uploader_id 7380187756258935814), credited to the sound "original sound - realpython" by artist realpython, with a 341-character description that links https://realpython.com/podcasts/rpp/311 and credits "Chris Trudeau (hosted by Chris Bailey)" under twelve hashtags ending #comparison #polars #sql. At capture time, the mtime of metadata.json, epoch 1792388422 or 2026-10-19 05:40:22 UTC and so 36 hours 40 minutes 22 seconds after posting, the clip held 421 views, 5 likes, 1 comment, 2 reposts and 5 saves. I read 13 of the 38 extracted frames: 001, 004, 007, 010, 013, 016, 019, 022, 026, 029, 032, 036 and 038, which at the 0.5 fps extraction rate sit at 0, 6, 12, 18, 24, 30, 36, 42, 50, 56, 62, 70 and 74 seconds. The transcript runs 250 words across 21 segments. Every frame I read shows the same shot: one man with grey hair, a grey beard and black-framed glasses, wearing large over-ear headphones, speaking to a webcam in a home music studio with four guitars on the wall behind him, a synthesizer and mixing desk over his right shoulder, and a small amplifier on a shelf at upper left. A black banner pinned at the top third reads labs.quansight.org/blog/polars-vs-sql-differences in every single frame from 001 through 038. A tilted white sticker above it reads "Polars vs SQL" from frame 001 through frame 032; by frame 036 it has been replaced with a white rounded card bearing the Real Python logo and the text "Follow us for more Real Python Podcast clips", and by frame 038 that card is mid-animation and blank. Burned-in captions advance two or three words at a time across the bottom: "POLARS DATAFRAMES ARE" (001), "UNORDERED BAG OF ROWS" (004), "COLUMN OPERATIONS" (007), "BUT IF YOU THINK ABOUT" (010), "MAY MAINTAIN ORDER" (013), "TO WRITE" (016), "EXPRESSIONS" (019), "IN POLARS PL.LIT(1)" (022), "IN THIS TABLE" (026), "THIS IS CONFIGURABLE" (029), "NULLS_FIRST = TRUE" (032), "THE SUM OF ZERO ELEMENTS" (036) and "AND ZERO IN POLARS" (038). No terminal, no code editor, no DataFrame output, no SQL query and no browser window appears in any frame I read; the clip is a talking head and a URL banner, and it never shows a single one of the code examples it describes. The spoken argument is a secondhand summary of a Quansight Labs blog post: Polars DataFrames are collections of columns with defined row order, SQL tables are unordered bags of atomic rows, and four concrete gotchas follow when you port between them, covering row-order preservation, length-changing expressions, literal semantics, null sort order and the sum of an empty set. The Whisper transcript ends at 99.98 s against an audio track that ffprobe measures at 75.520000 s for audio.mp3 and 75.477007 s for the muxed stream, so the final nine segments all end past the end of the audio and the final seven of them, running 76.96 s to 99.98 s, have no audio behind them at all and are invented; nothing below quotes them.
The clip is 75 seconds of a 47-minute episode published 36 days earlier, and the episode title has nothing to do with Polars
The TikTok description points at https://realpython.com/podcasts/rpp/311. That page sits behind a Cloudflare challenge and returned HTTP 403 to every fetch I attempted, so I read the show's RSS feed at https://realpython.com/podcasts/rpp/feed instead, which returned 2,816,863 bytes and 316 items without a challenge. The feed item carrying <itunes:episode>311</itunes:episode> is titled "Django Developers Survey Results & Reproducible Python Builds", with pubDate of "Fri, 11 Sep 2026 12:00:00 +0000", an <itunes:duration> of 2810 seconds (46 minutes 50 seconds), and an enclosure of 45,969,631 bytes at RPP_E311_03_PyCoders.303c777df801.mp3. The episode is a PyCoder's Weekly roundup, not a Polars episode. Polars is one of roughly a dozen items in it.
The feed's chapter list puts the relevant segment precisely: "00:09:56 – The Polars vs SQL Differences Nobody Is Talking About", with the next marker at "00:16:55 – Sponsor: Six Feet Up". That is a segment of 419 seconds, and the 75.477-second TikTok is about 18 percent of it. The feed's show-links block describes the item as "Some problems can be attacked with either SQL or Polars, but subtle differences in how the two mechanisms work can run you into trouble."
On names, the TikTok description writes "Chris Trudeau (hosted by Chris Bailey)". The primary sources use the full forms. The episode description in the feed reads "Christopher Trudeau is back on the show this week with another batch of PyCoder's Weekly articles and projects", and the channel-level description reads "A weekly Python podcast hosted by Christopher Bailey with interviews, coding tips, and conversation with guests from the Python community." I did not verify which of the two men is the face in the frames. The speaker refers to the article author in the third person at 29.02 s, "His suggestion is to write maximally safe pollards", which matches the guest's role of presenting other people's articles, but a frame of a bearded man in headphones is not a primary source for an identity.
The episode was published 2026-09-11 and the clip was posted 2026-10-17, a gap of 36 days. The underlying blog post is older still: Marco Gorelli published it on Quansight Labs on August 19, 2026, 23 days before the episode and 59 days before the TikTok. The post's actual title is sentence case, "The Polars vs SQL differences nobody is talking about"; the podcast show notes title-case it.
Throughout, Whisper renders "Polars" as "Pollard's" or "pollards" in every occurrence. The burned-in captions get it right, and the caption at frame 022 reads "IN POLARS PL.LIT(1)", which is how I know the transcript's "lit1" at 41.96 s is the call pl.lit(1).
Correction: nulls_first is not a Polars argument, and the clip states the polarity backwards
This is the only instruction in the clip that a developer could copy directly, and it does not work.
At 56.48 s the video says "This is configurable, I guess, in pollards, you would put nulls underscore first equals true, or the reverse, nulls first equals false." The burned-in caption at frame 032, 62 seconds in, renders this as NULLS_FIRST = TRUE in large white text, so the claim is on screen as well as in the audio.
Polars has no nulls_first parameter. The polars.DataFrame.sort API reference gives the signature as DataFrame.sort(by, *more_by, descending=False, nulls_last=False, multithreaded=True, maintain_order=False) and documents the null argument as nulls_last: "Place null values last; can specify a single boolean applying to all columns or a sequence of booleans for per-column control." The polars.Expr.sort reference gives Expr.sort(*, descending=False, nulls_last=False) with the same argument name. On a clean Polars 2.0.0 install, calling pl.DataFrame({"v":[2,None,1]}).sort("v", nulls_first=True) raises TypeError: DataFrame.sort() got an unexpected keyword argument 'nulls_first'.
The polarity is also inverted. Because the real argument is nulls_last and it defaults to False, nulls-first is what you already get for free. On Polars 2.0.0, pl.DataFrame({"v":[2,None,1]}).sort("v") returns null, 1, 2. To get the SQL default you pass nulls_last=True, which returns 1, 2, null. So the correct porting advice is the mirror image of what the clip says: you do not set anything to get Polars' nulls-first behavior, and you set nulls_last=True to match SQL.
The error traces back past the speaker. Gorelli's post itself writes "This is configurable (nulls_first=True / nulls_first=False)", so the clip is faithfully repeating a mistake in its source rather than introducing one. The two surrounding sentences in the clip are correct: at 51.52 s, "If you sort a table in SQL, the null comes last by default", and at 55.22 s, "In pollards, they come first." I confirmed both. DuckDB 1.5.6 running from n select v order by v over the same three values returns 1, 2, NULL.
Verified by execution: the literal, empty-sum and column-independence claims all reproduce
The clip makes four factual assertions about Polars semantics beyond the null-ordering one. I ran all of them against Polars 2.0.0 and DuckDB 1.5.6 rather than taking either the video or the blog post on authority.
At 41.96 s the video says "In pollards, lit1 means a single row with a value of 1, whereas in SQL, it means repeat the value of 1 in every row in this table." Correct. Over a three-row frame, df.select(sum_1 = pl.lit(1).sum()) returns a one-row result with sum_1 of 1. The equivalent DuckDB query, from df select sum(1) as sum_1, returns 3. Gorelli's post states this as "In Polars, lit(1) means 'a single row with the value of 1', whereas in SQL it means 'repeat the value 1 for every row in this table'", and his published output matches mine exactly, including the i32 dtype on the Polars side against int128 on the DuckDB side.
At 69.68 s the video says "The sum of zero elements is null in SQL and zero in pollards." Correct. pl.DataFrame({"x":[]}, schema={"x": pl.Int64}).select(pl.col("x").sum()) returns 0 in Polars, and select sum(x) as s from e over the same empty frame returns NULL in DuckDB. This is the last substantive line in the clip, carried by the captions at frames 036 and 038 as "THE SUM OF ZERO ELEMENTS" and "AND ZERO IN POLARS".
At 29.02 s and 36.74 s the video says "His suggestion is to write maximally safe pollards, then you're best off avoiding expressions, which would change the length. Expressions like drop nulls or change the order, like expression.sort." That is a fair paraphrase. Gorelli writes: "if you want to write maximally safe Polars, then you're best off avoiding expressions which change length (e.g. Expr.drop_nulls) or which change order (e.g. Expr.sort). Instead, use the dataframe equivalents (DataFrame.drop_nulls and DataFrame.sort respectively) so that all rows get truncated or rearranged together, or specify ignore_nulls and order_by arguments when available."
The underlying hazard reproduces. Over a three-row frame of musical theatre roles where Elsa has a null voice and Anna has a null role, df.select(pl.col('voice','role').drop_nulls()) returns two rows pairing "Mezzo soprano" with "lead" and "Tenor" with "supporting". The first of those pairs never existed in the input: Anna's voice got fused to Elsa's role because each column dropped its own null independently. The frame-level df.drop_nulls() returns the single intact row, Olaf. The arguments the post mentions do exist: on Polars 2.0.0, pl.Expr.over carries both order_by and nulls_last, and pl.Expr.first and pl.Expr.last carry ignore_nulls=False.
The clip drops this example entirely. The caption at frame 007 reads "COLUMN OPERATIONS" and at frame 019 "EXPRESSIONS", but no frame I read shows the DataFrame, the output, or any code at all. A viewer who has not read the post gets the warning without the demonstration that makes it land.
Partial correction: "some SQL engines may maintain order" undersells a documented DuckDB guarantee
At 22.06 s the video says "And some SQL engines may maintain order, but if you use different SQL engines, you might get different results." The caption at frame 013 is "MAY MAINTAIN ORDER".
The practical advice is sound, and the post states it the same way, telling readers that "if you want your solution to generalise across SQL engines, it's best to not rely on this." The word "may" is what undersells it. DuckDB's Order Preservation page opens with "For many operations, DuckDB preserves the order of rows, similarly to data frame libraries such as Pandas", and then publishes an explicit contract. The clauses that "guarantee that the original row order is preserved" are listed as COPY, FROM with a single table, LIMIT, OFFSET, SELECT, UNION ALL, WHERE, window functions with an empty OVER clause, and CTEs and table subqueries containing only those components. The operations that do not guarantee it are listed just as explicitly: FROM with multiple tables or subqueries, JOIN, UNION, USING SAMPLE, whole-table aggregation, GROUP BY, and ORDER BY. The page also documents the escape hatch, noting that row_number() OVER () "allows turning the original row order into an explicit column that can be referenced in the operations that don't preserve row order by default."
So the guarantee is per-clause, per-engine and written down, not a coin flip. The clip's conclusion still holds for portable code, and the hazard it points at is real: running Gorelli's partitioned cumulative-sum example, Polars 2.0.0 returned rows in the input order 1, 2, 3 while DuckDB 1.5.6 returned them as 1, 3, 2. Gorelli's published run of the identical query returned 2, 1, 3. Three runs, two row orders out of DuckDB alone, which makes the point better than the clip's hedge does.
At 8.94 s the video says "Pollard's row order is well-defined and can be preserved by select and with column operations." The post is firmer: "In Polars, not only is row-order well-defined, it is also preserved by select and with_columns operations." The post also carries the caveat the clip omits, that "Polars doesn't always promise to preserve it", which is exactly what the column-independence example demonstrates.
Context the clip omits: Polars ships its own SQL interface, and on Polars 2.0.0 it follows SQL semantics on every difference the clip names
The clip is framed around "porting code" as a one-way move from SQL to Polars expressions. It never mentions that you do not have to choose. The Polars SQL Python API exposes pl.SQLContext, the module-level pl.sql(), DataFrame.sql(), LazyFrame.sql(), Series.sql() and pl.sql_expr(), and runs "SQL queries mediated by the Polars execution engine against Polars DataFrame, LazyFrame, and Series data, as well as Pandas DataFrame and Series, and PyArrow Table and RecordBatch objects". Separately, pl.read_database(query, connection, ...) and pl.read_database_uri(query, uri, ...) pull results out of an actual database into a Polars frame; I confirmed both signatures on the 2.0.0 install.
That matters for the clip's thesis, because on Polars 2.0.0 the SQL layer keeps SQL semantics rather than Polars semantics on all three differences the clip names. Running through pl.SQLContext: select sum(1) as sum_1 from df over a three-row frame returns 3, matching DuckDB rather than the 1 that pl.lit(1).sum() returns. select sum(x) as s from e over an empty frame returns null, matching DuckDB rather than the 0 that pl.col("x").sum() returns. select v from n order by v returns 1, 2, null, matching DuckDB's nulls-last default rather than Polars' nulls-first.
That alignment is recent enough to be worth dating. The Polars 0.20.2 install already on this machine behaves differently: pl.SQLContext(df=df).execute("select sum(1) as sum_1 from df", eager=True) returns 1 on 0.20.2 and 3 on 2.0.0. I am reporting what I observed on the two versions I ran, not a changelog entry I located, so the release that changed it is unverified. The operational point holds either way: if you are porting a SQL query and the literal, empty-sum or null-ordering semantics are what you care about, running it through pl.SQLContext on a current Polars preserves them, and rewriting it as expressions does not.
The lazy and eager distinction is not what any of these differences are about. The Polars lazy API guide states that in the eager API "the query is executed immediately" while in the lazy API "the query is only evaluated once it is collected", and that deferring lets the planner apply predicate pushdown, "Apply filters as early as possible while reading the dataset", and projection pushdown, "Select only the columns that are needed while reading the dataset". Every result above is identical eager or lazy. The gotchas the clip lists are semantic, not execution-mode artifacts.
On the expression model the clip leans on, the Polars expressions and contexts guide defines an expression as "a lazy representation of a data transformation" and names four contexts: select, with_columns, filter and group_by. It also states the rule that makes the literal behavior consistent rather than surprising, that "expressions in a context select must produce series that are all the same length or they must produce a scalar", and that "Scalars will be broadcast to match the length of the remaining series. Literals, like the number used above, are also broadcast."
The clip makes no performance claim of any kind. No benchmark, no speed multiple, no memory figure appears in the transcript or in any caption I read. Nothing to check, and that is to its credit.
The last 25 seconds of the transcript are Whisper output with no audio underneath it
The transcript file runs to a final segment ending at 99.98 s. The audio does not. ffprobe reports audio.mp3 at 75.520000 s and the muxed AAC stream at 75.477007 s. Nine of the 21 segments end past 75.52 s, and seven of them begin past it. Segment 15 starts at 76.96 s with "liable up here". Then a 12-second gap, and segments 16 through 21 run from 89.52 s to 99.98 s: "You wait for a minute, if you'll break questions, you're welcome", "Take a break", "You wait for a minute", "But see, there's...", "We should be able to see the difference", "We should cover a little later...". None of that is in the file. It is the decoder's language model continuing past the end of the signal, and the 12-second silent gap before it is the tell.
The last real line is segment 12, 69.68 s to 75.32 s, "The sum of zero elements is null in SQL and zero in pollards", corroborated by the captions at frames 036 and 038. Segments 13 and 14, "You're welcome" and "All right", straddle the boundary and are probably a clipped trailing word plus hallucination. Nothing at or after 76.96 s should be quoted as evidence of what the episode contains, and no section above does so.
Key Takeaways
- Correction: At 56.48 s the clip says to set "nulls underscore first equals true", and frame 032 puts
NULLS_FIRST = TRUEon screen. Polars has nonulls_firstargument.polars.DataFrame.sortandpolars.Expr.sorttakenulls_last, which defaults toFalse, and on Polars 2.0.0 passingnulls_first=TrueraisesTypeError: DataFrame.sort() got an unexpected keyword argument 'nulls_first'. The correct porting move isnulls_last=Trueto match SQL, and nothing at all to keep Polars' nulls-first default. - Context: The error originates in the source post, not the speaker. Marco Gorelli's Quansight Labs article writes "(
nulls_first=True/nulls_first=False)" and the clip repeats it accurately. - Verified: The literal claim at 41.96 s, the empty-sum claim at 69.68 s and the null default claim at 51.52 s and 55.22 s, all run on Polars 2.0.0 against DuckDB 1.5.6.
pl.lit(1).sum()over three rows returns 1 wheresum(1)returns 3; the sum of an empty Int64 column returns 0 where SQL returnsNULL;DataFrame.sortputs nulls first whereorder byputs them last. - Verified: The length-changing-expression warning at 29.02 s and 36.74 s.
df.select(pl.col('voice','role').drop_nulls())produced a voice-and-role pairing that never appeared in the source data, whiledf.drop_nulls()returned the one intact row. - Partial correction: At 22.06 s the clip says "some SQL engines may maintain order". DuckDB publishes an explicit per-clause contract:
SELECT,WHERE,LIMIT,OFFSET,FROMwith a single table,UNION ALL,COPYand empty-OVERwindow functions preserve order, whileJOIN,GROUP BY,UNION,ORDER BY,USING SAMPLEand multi-tableFROMdo not. The portability advice still holds. - Clarification: The TikTok description writes "Chris Trudeau (hosted by Chris Bailey)". The Real Python feed uses "Christopher Trudeau" and describes the show as "hosted by Christopher Bailey".
- Clarification: Episode #311 is "Django Developers Survey Results & Reproducible Python Builds", published 2026-09-11 12:00 UTC, 2,810 seconds long. The Polars segment runs 00:09:56 to 00:16:55, so this clip is about 18 percent of a 419-second segment in a 47-minute roundup, posted 36 days later.
- Context: The clip never mentions that Polars has a SQL interface.
pl.SQLContext,pl.sql(),DataFrame.sql(),LazyFrame.sql(),Series.sql(),pl.sql_expr(),pl.read_database()andpl.read_database_uri()all exist, and on Polars 2.0.0 that layer returned 3 forsum(1),nullfor the empty sum, and nulls-last fororder by, matching SQL on all three. - Unverified: Which of the two men is on camera. The speaker refers to the article author in the third person at 29.02 s, which matches the guest's role, but I did not confirm the face against a primary source.
- Unverified: The exact Polars release in which
SQLContextswitched to SQL literal semantics. I observedselect sum(1)returning 1 on 0.20.2 and 3 on 2.0.0, and did not locate the changelog entry. - Context: The clip makes no performance claim. No benchmark, speed multiple or memory figure appears in the transcript or in any caption I read.
- Context: The transcript's final seven segments, 76.96 s through 99.98 s, are hallucinated past an audio track that ends at 75.52 s. Whisper also renders "Polars" as "Pollard's" throughout; the caption at frame 022, "IN POLARS PL.LIT(1)", disambiguates the transcript's "lit1".
Resources
- The Polars vs SQL differences nobody is talking about, Marco Gorelli, Quansight Labs, August 19, 2026 is the article the clip summarizes and the URL burned into every frame. It establishes the mental-model, row-order, column-independence, literal, null-sorting and empty-sum claims I quote, including the
nulls_firstwording I correct. - The Real Python Podcast RSS feed, Real Python establishes episode #311's number, title,
pubDateof Fri, 11 Sep 2026 12:00:00 +0000, 2,810-second duration, enclosure size, the "00:09:56 – The Polars vs SQL Differences Nobody Is Talking About" chapter marker, the "Christopher Trudeau" spelling and the "hosted by Christopher Bailey" channel description. I used the feed becausehttps://realpython.com/podcasts/rpp/311/returned HTTP 403 behind a Cloudflare challenge to every fetch I attempted, and I do not cite that page. - polars.DataFrame.sort API reference, Polars documentation establishes the signature
sort(by, *more_by, descending=False, nulls_last=False, multithreaded=True, maintain_order=False)and thenulls_lastdescription I quote. - polars.Expr.sort API reference, Polars documentation establishes
Expr.sort(*, descending=False, nulls_last=False), confirming nonulls_firstexists at expression level either. - SQL interface Python API, Polars documentation establishes the
SQLContext,pl.sql,DataFrame.sql,LazyFrame.sql,Series.sqlandpl.sql_exprentry points. - Lazy API, Polars user guide establishes the eager-versus-lazy definitions and the pushdown optimizations I quote.
- Expressions and contexts, Polars user guide establishes the definition of an expression, the four contexts, the same-length-or-scalar rule, and literal broadcasting.
- Order Preservation, DuckDB documentation establishes the per-clause list of operations that do and do not guarantee row order, and the
row_number() OVER ()workaround. It is the source the Quansight post links and the basis for my partial correction. - Local execution on Polars 2.0.0 with DuckDB 1.5.6 and PyArrow in a clean virtualenv, plus the Polars 0.20.2 install already on this machine, produced every result I report as run: the
TypeErroronnulls_first, the 1-versus-3 literal sum, the 0-versus-NULLempty sum, the null ordering, the fused voice-and-role rows, the DuckDB row reordering, and theSQLContextresults that differ between 0.20.2 and 2.0.0.
Published October 17, 2026. Writeup generated from a favorited TikTok.