> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-parallel-read-in-order-multi-part.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Explorez ClickHouse à l’aide d’un jeu de données portant sur des milliards de trajets en taxi et en VTC (Uber, Lyft, etc.) au départ de New York depuis 2009

# Analyse géospatiale avec le jeu de données des taxis new-yorkais

export const RunnableCode = ({children, run = false, showStats = true}) => {
  const [results, setResults] = useState(null);
  const [error, setError] = useState(null);
  const [loading, setLoading] = useState(false);
  const [showResults, setShowResults] = useState(false);
  const [stats, setStats] = useState(null);
  const [isDark, setIsDark] = useState(false);
  const [hoveredRow, setHoveredRow] = useState(-1);
  const codeRef = useRef(null);
  useEffect(() => {
    if (typeof window !== "undefined") {
      const check = () => setIsDark(document.documentElement.classList.contains("dark"));
      check();
      const observer = new MutationObserver(check);
      observer.observe(document.documentElement, {
        attributes: true,
        attributeFilter: ["class"]
      });
      return () => observer.disconnect();
    }
  }, []);
  useEffect(() => {
    if (codeRef.current) {
      const block = codeRef.current.querySelector(".code-block");
      if (block) {
        block.style.marginBottom = "0";
        block.style.marginTop = "0";
        block.style.borderBottomLeftRadius = "0";
        block.style.borderBottomRightRadius = "0";
      }
    }
  });
  const getSqlText = () => {
    if (!codeRef.current) return "";
    const code = codeRef.current.querySelector("code");
    return (code || codeRef.current).textContent.trim();
  };
  const executeQuery = async () => {
    const sql = getSqlText();
    if (!sql) return;
    setLoading(true);
    setError(null);
    setResults(null);
    setShowResults(true);
    try {
      const cleanQuery = sql.replace(/;$/, "").trim();
      const params = new URLSearchParams({
        query: cleanQuery,
        default_format: "JSONCompact",
        result_overflow_mode: "break",
        read_overflow_mode: "break",
        allow_experimental_analyzer: "1"
      });
      const res = await fetch(`https://sql-clickhouse.clickhouse.com/?${params.toString()}`, {
        method: "POST",
        headers: {
          Authorization: `Basic ${btoa(`demo:`)}`
        }
      });
      const text = await res.text();
      if (!res.ok) {
        setError(text || `HTTP ${res.status}`);
        setLoading(false);
        return;
      }
      const json = JSON.parse(text);
      setResults(json);
      setStats(json.statistics || null);
    } catch (err) {
      setError(err.message || "Échec de l'exécution de la requête");
    }
    setLoading(false);
  };
  useEffect(() => {
    if (run) executeQuery();
  }, []);
  const formatRows = n => {
    if (n >= 1e9) return `${(n / 1e9).toFixed(1)}B`;
    if (n >= 1e6) return `${(n / 1e6).toFixed(1)}M`;
    if (n >= 1e3) return `${(n / 1e3).toFixed(1)}K`;
    return String(n);
  };
  const formatBytes = b => {
    if (b >= 1e9) return `${(b / 1e9).toFixed(2)} GB`;
    if (b >= 1e6) return `${(b / 1e6).toFixed(2)} MB`;
    if (b >= 1e3) return `${(b / 1e3).toFixed(2)} KB`;
    return `${b} B`;
  };
  const isNumericType = type => {
    return (/^(UInt|Int|Float|Decimal)/).test(type);
  };
  const isHyperlink = value => {
    return typeof value === "string" && (/^https?:\/\//).test(value);
  };
  const computeColumnExtremes = (meta, data) => {
    const extremes = {};
    for (let i = 0; i < meta.length; i++) {
      if (isNumericType(meta[i].type)) {
        let min = Infinity, max = -Infinity;
        for (const row of data) {
          const v = Number(row[i]);
          if (!isNaN(v)) {
            if (v < min) min = v;
            if (v > max) max = v;
          }
        }
        if (max > -Infinity) {
          extremes[i] = {
            min,
            max
          };
        }
      }
    }
    return extremes;
  };
  const computeColumnWidths = (meta, data) => {
    const lengths = meta.map((col, i) => {
      const headerLen = col.name.length + col.type.length + 1;
      let maxData = 0;
      for (const row of data) {
        const v = row[i];
        const len = v === null ? 4 : String(v).length;
        if (len > maxData) maxData = len;
      }
      return Math.max(headerLen, maxData);
    });
    const total = lengths.reduce((s, l) => s + l, 0);
    return lengths.map(l => `${(l / total * 100).toFixed(1)}%`);
  };
  const copyResultsAsTSV = () => {
    if (!results || !results.meta || !results.data) return;
    const header = results.meta.map(col => col.name).join("\t");
    const rows = results.data.map(row => row.map(cell => cell === null ? "NULL" : String(cell)).join("\t"));
    const tsv = [header, ...rows].join("\n");
    navigator.clipboard.writeText(tsv);
  };
  const borderColor = isDark ? "rgba(255,255,255,0.15)" : "#e5e7eb";
  const bgColor = isDark ? "rgba(255,255,255,0.05)" : "#f9fafb";
  const headerBg = isDark ? "#2a2a2a" : "#f3f4f6";
  const textColor = isDark ? "#e5e7eb" : "#1f2937";
  const mutedColor = isDark ? "#d1d5db" : "#6b7280";
  const accentColor = isDark ? "#FAFF69" : "#323232";
  const accentTextColor = isDark ? "#000" : "#fff";
  const barColor = isDark ? "#35372f" : "#d2d2d2";
  const cellBg = isDark ? "#1f201b" : "#ffffff";
  const cellBgHover = isDark ? "lch(15.8 0 0)" : "#f0f0f0";
  const extremes = results && results.meta && results.data ? computeColumnExtremes(results.meta, results.data) : {};
  const colWidths = results && results.meta && results.data ? computeColumnWidths(results.meta, results.data) : [];
  const getCellBarStyle = (cell, ci, ri) => {
    if (cell === null) return null;
    const colMeta = results.meta[ci];
    if (!isNumericType(colMeta.type) || !extremes[ci] || results.data.length <= 1 || extremes[ci].max <= 0) return null;
    const ratio = 100 * Number(cell) / extremes[ci].max;
    const bg = ri === hoveredRow ? cellBgHover : cellBg;
    return {
      background: `linear-gradient(to right, ${barColor} 0%, ${barColor} ${ratio}%, ${bg} ${ratio}%, ${bg} 100%)`
    };
  };
  const renderCell = (cell, ci) => {
    if (cell === null) {
      return <span style={{
        color: mutedColor,
        fontStyle: "italic"
      }}>NULL</span>;
    }
    const value = String(cell);
    if (isHyperlink(value)) {
      return <a href={value} target="_blank" rel="noopener noreferrer" style={{
        color: accentColor,
        textDecoration: "underline",
        cursor: "pointer"
      }}>
          {value}
        </a>;
    }
    return value;
  };
  return <div className="not-prose" style={{
    margin: "1rem 0",
    width: "100%",
    boxSizing: "border-box",
    contain: "inline-size"
  }}>
      {}
      <div>
        <div ref={codeRef}>{children}</div>

        {}
        <div style={{
    display: "flex",
    justifyContent: "space-between",
    alignItems: "center",
    padding: "6px 12px",
    backgroundColor: headerBg,
    borderWidth: "0 1px 1px 1px",
    borderStyle: "solid",
    borderColor: isDark ? "rgba(255,255,255,0.1)" : "rgba(11,11,11,0.1)",
    borderRadius: "0 0 4px 4px"
  }}>
          <div style={{
    display: "flex",
    alignItems: "center",
    gap: "12px"
  }}>
            {results && <button onClick={() => setShowResults(!showResults)} style={{
    background: "none",
    border: "none",
    cursor: "pointer",
    color: mutedColor,
    fontSize: "12px",
    padding: "2px 4px"
  }}>
                {showResults ? "▼ Masquer les résultats" : "▶ Afficher les résultats"}
              </button>}
            {showStats && stats && <span style={{
    fontSize: "11px",
    color: mutedColor,
    fontStyle: "italic"
  }}>
                {formatRows(stats.rows_read)} lignes lues, {formatBytes(stats.bytes_read)} en {stats.elapsed.toFixed(3)}s
              </span>}
          </div>
          <button onClick={() => executeQuery()} disabled={loading} style={{
    display: "flex",
    alignItems: "center",
    gap: "6px",
    padding: "4px 14px",
    borderRadius: "4px",
    border: "none",
    cursor: loading ? "wait" : "pointer",
    backgroundColor: accentColor,
    color: accentTextColor,
    fontSize: "12px",
    fontWeight: 600
  }}>
            {loading ? <span>En cours...</span> : <>
                <span style={{
    fontSize: "10px"
  }}>▶</span>
                <span>Exécuter</span>
              </>}
          </button>
        </div>
      </div>

      {}
      {showResults && <div className="not-prose" style={{
    marginTop: "8px",
    maxHeight: "350px",
    overflow: "auto",
    border: `1px solid ${borderColor}`,
    borderRadius: "4px"
  }}>
          <div>
            {loading && <div style={{
    padding: "24px",
    textAlign: "center",
    color: mutedColor
  }}>Exécution de la requête...</div>}

            {error && <div style={{
    padding: "12px 16px",
    color: "#ef4444",
    backgroundColor: isDark ? "rgba(239,68,68,0.1)" : "#fef2f2",
    fontSize: "13px",
    fontFamily: "monospace",
    whiteSpace: "pre-wrap"
  }}>
                {error}
              </div>}

            {results && results.meta && results.data && <div style={{
    display: "grid",
    gridTemplateColumns: colWidths.join(" "),
    width: "100%",
    fontSize: "13px",
    fontFamily: 'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace'
  }}>
                {results.meta.map((col, i) => <div key={`h-${i}`} style={{
    position: "sticky",
    top: 0,
    zIndex: 1,
    padding: "6px 12px",
    textAlign: isNumericType(col.type) && results.meta.length > 1 ? "right" : "left",
    backgroundColor: headerBg,
    borderBottom: `1px solid ${borderColor}`,
    color: textColor,
    fontWeight: 600,
    fontSize: "12px",
    whiteSpace: "nowrap",
    overflow: "hidden",
    textOverflow: "ellipsis"
  }}>
                    {col.name}
                    <span style={{
    color: mutedColor,
    fontWeight: 400,
    marginLeft: "4px",
    fontSize: "10px"
  }}>{col.type}</span>
                  </div>)}
                {results.data.map((row, ri) => row.map((cell, ci) => <div key={`${ri}-${ci}`} onMouseEnter={() => setHoveredRow(ri)} onMouseLeave={() => setHoveredRow(-1)} style={{
    padding: "4px 12px",
    color: textColor,
    whiteSpace: "nowrap",
    overflow: "hidden",
    textOverflow: "ellipsis",
    textAlign: isNumericType(results.meta[ci].type) && results.meta.length > 1 ? "right" : "left",
    borderBottom: `1px solid ${borderColor}`,
    backgroundColor: ri === hoveredRow ? cellBgHover : ri % 2 === 0 ? "transparent" : bgColor,
    ...getCellBarStyle(cell, ci, ri)
  }}>
                      {renderCell(cell, ci)}
                    </div>))}
              </div>}

            {results && results.data && <div style={{
    display: "flex",
    justifyContent: "space-between",
    alignItems: "center",
    padding: "4px 12px",
    fontSize: "11px",
    color: mutedColor,
    borderTop: `1px solid ${borderColor}`,
    backgroundColor: headerBg
  }}>
                <span>
                  {results.rows} ligne{results.rows !== 1 ? "s" : ""}
                </span>
                <button onClick={copyResultsAsTSV} style={{
    background: "none",
    border: "none",
    cursor: "pointer",
    color: mutedColor,
    fontSize: "11px",
    padding: "2px 6px",
    borderRadius: "3px"
  }} onMouseEnter={e => e.target.style.color = textColor} onMouseLeave={e => e.target.style.color = mutedColor}>
                  ⧉ Copier en TSV
                </button>
              </div>}
          </div>
        </div>}
    </div>;
};

<View title="Cloud">
  Dans ce tutoriel, vous découvrirez comment utiliser ClickHouse pour exécuter des requêtes analytiques sur de grands volumes de données.
  Vous verrez également comment utiliser un dictionnaire pour enrichir les données et rédiger des requêtes avec jointures.

  ## Prérequis

  Pour ce tutoriel, vous aurez besoin des éléments suivants :

  * Un [compte ClickHouse Cloud](https://clickhouse.cloud/signUp?loc=docs-sample-datasets-nyc-taxi) (300 \$ de crédits offerts à l'inscription)
  * [Un service ClickHouse Cloud](/fr/get-started/setup/cloud#1-create-a-clickhouse-service)

  <Steps titleSize="h2">
    <Step title="Créez la table" id="create-a-new-table">
      Le jeu de données utilisé dans ce tutoriel est celui des taxis de New York. Il contient des informations sur des millions de courses de taxi, avec des colonnes telles que le montant du pourboire, les péages, le type de paiement, etc.

      1. Sélectionnez **console SQL** dans le menu de gauche
      2. Cliquez sur l'onglet **+** à côté de l'icône d'accueil pour créer une nouvelle requête
      3. Dans l'éditeur SQL, saisissez la requête suivante, puis cliquez sur **Run** :

      ```sql Expandable theme={null}
      CREATE TABLE trips
      (
          `trip_id` UInt32,
          `vendor_id` Enum8('1' = 1, '2' = 2, '3' = 3, '4' = 4, 'CMT' = 5, 'VTS' = 6, 'DDS' = 7, 'B02512' = 10, 'B02598' = 11, 'B02617' = 12, 'B02682' = 13, 'B02764' = 14, '' = 15),
          `pickup_date` Date,
          `pickup_datetime` DateTime,
          `dropoff_date` Date,
          `dropoff_datetime` DateTime,
          `store_and_fwd_flag` UInt8,
          `rate_code_id` UInt8,
          `pickup_longitude` Float64,
          `pickup_latitude` Float64,
          `dropoff_longitude` Float64,
          `dropoff_latitude` Float64,
          `passenger_count` UInt8,
          `trip_distance` Float64,
          `fare_amount` Float32,
          `extra` Float32,
          `mta_tax` Float32,
          `tip_amount` Float32,
          `tolls_amount` Float32,
          `ehail_fee` Float32,
          `improvement_surcharge` Float32,
          `total_amount` Float32,
          `payment_type` Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4),
          `trip_type` UInt8,
          `pickup` FixedString(25),
          `dropoff` FixedString(25),
          `cab_type` Enum8('yellow' = 1, 'green' = 2, 'uber' = 3),
          `pickup_nyct2010_gid` Int8,
          `pickup_ctlabel` Float32,
          `pickup_borocode` Int8,
          `pickup_ct2010` String,
          `pickup_boroct2010` String,
          `pickup_cdeligibil` String,
          `pickup_ntacode` FixedString(4),
          `pickup_ntaname` String,
          `pickup_puma` UInt16,
          `dropoff_nyct2010_gid` UInt8,
          `dropoff_ctlabel` Float32,
          `dropoff_borocode` UInt8,
          `dropoff_ct2010` String,
          `dropoff_boroct2010` String,
          `dropoff_cdeligibil` String,
          `dropoff_ntacode` FixedString(4),
          `dropoff_ntaname` String,
          `dropoff_puma` UInt16
      )
      ENGINE = MergeTree
      PARTITION BY toYYYYMM(pickup_date)
      ORDER BY pickup_datetime;
      ```
    </Step>

    <Step title="Insérez les données" id="add-the-dataset">
      Maintenant que vous avez créé une table, ajoutez-y les données des taxis de New York à partir de fichiers CSV stockés dans S3.

      La commande suivante insère environ 2 000 000 de lignes dans votre table trips à partir de deux fichiers distincts stockés dans S3 : `trips_1.tsv.gz` et `trips_2.tsv.gz` :

      ```sql Expandable theme={null}
      INSERT INTO trips
      SELECT * FROM s3(
          'https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_{1..2}.gz',
          'TabSeparatedWithNames', "
          `trip_id` UInt32,
          `vendor_id` Enum8('1' = 1, '2' = 2, '3' = 3, '4' = 4, 'CMT' = 5, 'VTS' = 6, 'DDS' = 7, 'B02512' = 10, 'B02598' = 11, 'B02617' = 12, 'B02682' = 13, 'B02764' = 14, '' = 15),
          `pickup_date` Date,
          `pickup_datetime` DateTime,
          `dropoff_date` Date,
          `dropoff_datetime` DateTime,
          `store_and_fwd_flag` UInt8,
          `rate_code_id` UInt8,
          `pickup_longitude` Float64,
          `pickup_latitude` Float64,
          `dropoff_longitude` Float64,
          `dropoff_latitude` Float64,
          `passenger_count` UInt8,
          `trip_distance` Float64,
          `fare_amount` Float32,
          `extra` Float32,
          `mta_tax` Float32,
          `tip_amount` Float32,
          `tolls_amount` Float32,
          `ehail_fee` Float32,
          `improvement_surcharge` Float32,
          `total_amount` Float32,
          `payment_type` Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4),
          `trip_type` UInt8,
          `pickup` FixedString(25),
          `dropoff` FixedString(25),
          `cab_type` Enum8('yellow' = 1, 'green' = 2, 'uber' = 3),
          `pickup_nyct2010_gid` Int8,
          `pickup_ctlabel` Float32,
          `pickup_borocode` Int8,
          `pickup_ct2010` String,
          `pickup_boroct2010` String,
          `pickup_cdeligibil` String,
          `pickup_ntacode` FixedString(4),
          `pickup_ntaname` String,
          `pickup_puma` UInt16,
          `dropoff_nyct2010_gid` UInt8,
          `dropoff_ctlabel` Float32,
          `dropoff_borocode` UInt8,
          `dropoff_ct2010` String,
          `dropoff_boroct2010` String,
          `dropoff_cdeligibil` String,
          `dropoff_ntacode` FixedString(4),
          `dropoff_ntaname` String,
          `dropoff_puma` UInt16
      ") SETTINGS input_format_try_infer_datetimes = 0
      ```

      Attendez la fin de l'insertion des données. Environ 150 Mo de données seront téléchargés.
      Une fois l'insertion des données terminée, vérifiez le nombre de lignes de la table `trips` :

      ```sql theme={null}
      SELECT count() FROM trips
      ```

      Vous devriez obtenir 1 999 657 lignes
    </Step>

    <Step title="Analysez les données" id="analyze-the-data">
      Une fois les données chargées, vous pouvez exécuter quelques requêtes pour les analyser.

      * Calculer le montant moyen des pourboires :
        ```sql theme={null}
        SELECT round(avg(tip_amount), 2) FROM trips
        ```

      * Calculer le coût moyen en fonction du nombre de passagers :
        ```sql theme={null}
        SELECT
            passenger_count,
            ceil(avg(total_amount),2) AS average_total_amount
        FROM trips
        GROUP BY passenger_count
        ```

      * Calculer le nombre quotidien de prises en charge par quartier :
        ```sql theme={null}
        SELECT
          pickup_date,
          pickup_ntaname,
          SUM(1) AS number_of_trips
        FROM trips
        GROUP BY pickup_date, pickup_ntaname
        ORDER BY pickup_date ASC
        ```

      * Calculer la durée de chaque trajet en minutes, puis regrouper les résultats selon cette durée :
        ```sql theme={null}
        SELECT
          avg(tip_amount) AS avg_tip,
          avg(fare_amount) AS avg_fare,
          avg(passenger_count) AS avg_passenger,
          count() AS count,
          truncate(date_diff('second', pickup_datetime, dropoff_datetime)/60) as trip_minutes
        FROM trips
        WHERE trip_minutes > 0
        GROUP BY trip_minutes
        ORDER BY trip_minutes DESC
        ```

      * Afficher le nombre de prises en charge dans chaque quartier, ventilé par heure de la journée :
        ```sql theme={null}
        SELECT
            pickup_ntaname,
            toHour(pickup_datetime) as pickup_hour,
            SUM(1) AS pickups
        FROM trips
        WHERE pickup_ntaname != ''
        GROUP BY pickup_ntaname, pickup_hour
        ORDER BY pickup_ntaname, pickup_hour
        ```
    </Step>

    <Step title="Créer un dictionnaire" id="create-a-dictionary">
      Vous allez ensuite créer un dictionnaire (un ensemble de paires clé-valeur stockées en mémoire) nommé `taxi_zone_dictionary` afin d'établir une correspondance entre les identifiants de lieu et les noms des boroughs de NYC, à partir d'un fichier CSV répertoriant tous les quartiers de New York.
      Ces identifiants correspondent aux colonnes `pickup_nyct2010_gid` et `dropoff_nyct2010_gid` de la table trips.

      Voici un extrait, présenté sous forme de tableau, du fichier CSV que vous utilisez. La colonne `LocationID` du fichier correspond aux colonnes `pickup_nyct2010_gid` et `dropoff_nyct2010_gid` de votre table `trips` :

      | LocationID | Borough | Zone | service\_zone |
      | - | - | - | - |
      | 1 | EWR | Newark Airport | EWR |
      | 2 | Queens | Jamaica Bay | Boro Zone |
      | 3 | Bronx | Allerton/Pelham Gardens | Boro Zone |
      | 4 | Manhattan | Alphabet City | Yellow Zone |
      | 5 | Staten Island | Arden Heights | Boro Zone |

      Exécutez la commande SQL suivante, qui crée un dictionnaire nommé `taxi_zone_dictionary` et le remplit à partir du fichier CSV stocké dans S3. L'URL du fichier est `https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/taxi_zone_lookup.csv`.

      ```sql theme={null}
      CREATE DICTIONARY taxi_zone_dictionary
      (
        `LocationID` UInt16 DEFAULT 0,
        `Borough` String,
        `Zone` String,
        `service_zone` String
      )
      PRIMARY KEY LocationID
      SOURCE(HTTP(URL 'https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/taxi_zone_lookup.csv' FORMAT 'CSVWithNames'))
      LIFETIME(MIN 0 MAX 0)
      LAYOUT(HASHED_ARRAY())
      ```

      <Note>
        Définir `LIFETIME` sur 0 désactive les mises à jour automatiques afin d'éviter tout trafic inutile vers notre bucket S3. Dans d'autres cas, vous pouvez le configurer différemment. Pour plus de détails, consultez [Rafraîchissement des données de dictionnaire avec LIFETIME](/fr/reference/statements/create/dictionary/lifetime).
      </Note>

      Vérifiez que tout a fonctionné. La requête suivante doit renvoyer 265 lignes, soit une ligne par quartier :

      ```sql theme={null}
      SELECT * FROM taxi_zone_dictionary
      ```
    </Step>

    <Step title="Exécutez des requêtes à l’aide du dictionnaire">
      Vous pouvez utiliser la fonction `dictGet` ([ou l'une de ses variantes](/fr/reference/functions/regular-functions/ext-dict-functions)) pour récupérer une valeur dans un dictionnaire.
      Il suffit de lui passer le nom du dictionnaire, la valeur souhaitée et la clé (qui, dans notre exemple, correspond à la colonne `LocationID` de `taxi_zone_dictionary`) pour obtenir la valeur correspondante.
      Par exemple, la requête suivante renvoie le `Borough` dont le `LocationID` est 132 (ce qui correspond à l'aéroport JFK) :

      ```sql theme={null}
      SELECT dictGet('taxi_zone_dictionary', 'Borough', 132)
      ```

      JFK se trouve dans le Queens. Notez que le temps nécessaire pour récupérer la valeur est pratiquement nul :

      ```response theme={null}
      ┌─dictGet('taxi_zone_dictionary', 'Borough', 132)─┐
      │ Queens                                          │
      └─────────────────────────────────────────────────┘

      1 rows in set. Elapsed: 0.004 sec.
      ```

      Utilisez la fonction `dictHas` pour vérifier si une clé existe dans le dictionnaire. Par exemple, la requête suivante renvoie `1` (ce qui équivaut à "true" dans ClickHouse) :

      ```sql theme={null}
      SELECT dictHas('taxi_zone_dictionary', 132)
      ```

      La requête suivante renvoie 0, car 4567 ne figure pas parmi les valeurs de `LocationID` dans le dictionnaire :

      ```sql theme={null}
      SELECT dictHas('taxi_zone_dictionary', 4567)
      ```

      Utilisez la fonction `dictGet` pour récupérer le nom d'un borough dans une requête. Par exemple :

      ```sql theme={null}
      SELECT
        count(1) AS total,
        dictGetOrDefault('taxi_zone_dictionary','Borough', toUInt64(pickup_nyct2010_gid), 'Unknown') AS borough_name
      FROM trips
      WHERE dropoff_nyct2010_gid = 132 OR dropoff_nyct2010_gid = 138
      GROUP BY borough_name
      ORDER BY total DESC
      ```

      Cette requête calcule le nombre total de courses de taxi par borough se terminant à l'aéroport de LaGuardia ou de JFK. Le résultat se présente comme suit ; notez qu'un nombre important de trajets ont un quartier de prise en charge inconnu :

      ```response theme={null}
      ┌─total─┬─borough_name──┐
      │ 23683 │ Unknown       │
      │  7053 │ Manhattan     │
      │  6828 │ Brooklyn      │
      │  4458 │ Queens        │
      │  2670 │ Bronx         │
      │   554 │ Staten Island │
      │    53 │ EWR           │
      └───────┴───────────────┘

      7 rows in set. Elapsed: 0.019 sec. Processed 2.00 million rows, 4.00 MB (105.70 million rows/s., 211.40 MB/s.)
      ```
    </Step>

    <Step title="Effectuer une jointure" id="perform-a-join">
      Enfin, écrivez quelques requêtes qui effectuent une jointure entre `taxi_zone_dictionary` et votre table `trips`.

      Commencez par un simple `JOIN` qui fonctionne de manière similaire à la requête précédente sur les aéroports :

      ```sql theme={null}
      SELECT
          count(1) AS total,
          Borough
      FROM trips
      JOIN taxi_zone_dictionary ON toUInt64(trips.pickup_nyct2010_gid) = taxi_zone_dictionary.LocationID
      WHERE dropoff_nyct2010_gid = 132 OR dropoff_nyct2010_gid = 138
      GROUP BY Borough
      ORDER BY total DESC
      ```

      La réponse est identique à celle de la requête `dictGet` :

      ```response theme={null}
      ┌─total─┬─Borough───────┐
      │  7053 │ Manhattan     │
      │  6828 │ Brooklyn      │
      │  4458 │ Queens        │
      │  2670 │ Bronx         │
      │   554 │ Staten Island │
      │    53 │ EWR           │
      └───────┴───────────────┘

      6 rows in set. Elapsed: 0.034 sec. Processed 2.00 million rows, 4.00 MB (59.14 million rows/s., 118.29 MB/s.)
      ```

      <Note>
        Notez que la sortie de la requête `JOIN` ci-dessus est identique à celle de la requête précédente, qui utilisait `dictGetOrDefault` (à ceci près que les valeurs `Unknown` n'y figurent pas).
        En coulisses, ClickHouse appelle en réalité la fonction `dictGet` sur le dictionnaire `taxi_zone_dictionary`, mais la syntaxe `JOIN` est plus familière aux développeurs SQL.
      </Note>

      Cette requête renvoie les lignes des 1 000 trajets aux pourboires les plus élevés, puis effectue une jointure interne entre chaque ligne et le dictionnaire :

      ```sql theme={null}
      SELECT *
      FROM trips
      JOIN taxi_zone_dictionary
      ON trips.dropoff_nyct2010_gid = taxi_zone_dictionary.LocationID
      WHERE tip_amount > 0
      ORDER BY tip_amount DESC
      LIMIT 1000
      ```

      <Tip>
        Privilégiez l'énumération des seules colonnes nécessaires à votre requête plutôt que d'utiliser SELECT \*. ClickHouse stockant les données par colonne, sélectionner moins de colonnes réduit directement le volume de données lues, décompressées et traitées.
      </Tip>
    </Step>
  </Steps>

  ## Prochaines étapes

  Pour en savoir plus sur ClickHouse, consultez la documentation suivante :

  * [Introduction aux index primaires dans ClickHouse](/fr/guides/clickhouse/data-modelling/sparse-primary-indexes) : découvrez comment ClickHouse utilise des index primaires creux pour localiser efficacement les données pertinentes lors de l'exécution des requêtes.
  * [Intégrer une source de données externe](/fr/integrations/home) : découvrez les options d'intégration de sources de données, notamment les fichiers, Kafka, PostgreSQL, les pipelines de données et bien d'autres.
  * [Visualiser des données dans ClickHouse](/fr/integrations/connectors/data-visualization/index) : connectez votre outil de visualisation ou de BI préféré à ClickHouse.
  * [Référence SQL](/fr/reference/home) : parcourez les fonctions SQL disponibles dans ClickHouse pour transformer, traiter et analyser vos données.
</View>

<View title="Open source">
  L’échantillon de données des taxis de New York regroupe plus de 3 milliards de trajets en taxi et en VTC (Uber, Lyft, etc.) au départ de New York depuis 2009. Ce guide de prise en main utilise un échantillon de 3 millions de lignes.

  Il existe plusieurs façons d’obtenir l’ensemble du jeu de données :

  * insérer les données directement dans ClickHouse Cloud depuis S3 ou GCS
  * télécharger des partitions préparées
  * Vous pouvez également interroger l’ensemble du jeu de données dans notre environnement de démonstration sur [sql.clickhouse.com](https://sql.clickhouse.com/?query=U0VMRUNUIGNvdW50KCkgRlJPTSBueWNfdGF4aS50cmlwcw\&chart=eyJ0eXBlIjoibGluZSIsImNvbmZpZyI6eyJ0aXRsZSI6IlRlbXBlcmF0dXJlIGJ5IGNvdW50cnkgYW5kIHllYXIiLCJ4YXhpcyI6InllYXIiLCJ5YXhpcyI6ImNvdW50KCkiLCJzZXJpZXMiOiJDQVNUKHBhc3Nlbmdlcl9jb3VudCwgJ1N0cmluZycpIn19).

  <Note>
    Les requêtes d’exemple ci-dessous ont été exécutées sur une instance **Production** de ClickHouse Cloud. Pour plus d’informations, consultez la section
    [« Spécifications du Playground »](/fr/get-started/sample-datasets/playground#specifications).
  </Note>

  ## Créer la table trips

  Commencez par créer une table pour les courses de taxi :

  ```sql theme={null}

  CREATE DATABASE nyc_taxi;

  CREATE TABLE nyc_taxi.trips_small (
      trip_id             UInt32,
      pickup_datetime     DateTime,
      dropoff_datetime    DateTime,
      pickup_longitude    Nullable(Float64),
      pickup_latitude     Nullable(Float64),
      dropoff_longitude   Nullable(Float64),
      dropoff_latitude    Nullable(Float64),
      passenger_count     UInt8,
      trip_distance       Float32,
      fare_amount         Float32,
      extra               Float32,
      tip_amount          Float32,
      tolls_amount        Float32,
      total_amount        Float32,
      payment_type        Enum('CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4, 'UNK' = 5),
      pickup_ntaname      LowCardinality(String),
      dropoff_ntaname     LowCardinality(String)
  )
  ENGINE = MergeTree
  PRIMARY KEY (pickup_datetime, dropoff_datetime);
  ```

  ## Charger les données directement depuis le stockage d’objet

  Vous pouvez récupérer un petit sous-ensemble des données (3 millions de lignes) pour vous familiariser avec elles. Les données sont stockées dans des fichiers TSV sur un stockage d’objet et peuvent facilement être transmises en flux vers
  ClickHouse Cloud à l’aide de la fonction de table `s3`.

  Les mêmes données sont stockées dans S3 et dans GCS ; vous pouvez choisir l’un ou l’autre onglet.

  <Tabs>
    <Tab title="S3">
      La commande suivante charge en flux trois fichiers depuis un bucket S3 dans la table `trips_small` (la syntaxe `{0..2}` est un caractère générique correspondant aux valeurs 0, 1 et 2) :

      ```sql theme={null}
      INSERT INTO nyc_taxi.trips_small
      SELECT
          trip_id,
          pickup_datetime,
          dropoff_datetime,
          pickup_longitude,
          pickup_latitude,
          dropoff_longitude,
          dropoff_latitude,
          passenger_count,
          trip_distance,
          fare_amount,
          extra,
          tip_amount,
          tolls_amount,
          total_amount,
          payment_type,
          pickup_ntaname,
          dropoff_ntaname
      FROM s3(
          'https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/trips_{0..2}.gz',
          'TabSeparatedWithNames'
      );
      ```
    </Tab>

    <Tab title="GCS">
      La commande suivante charge en flux trois fichiers depuis un bucket GCS dans la table `trips` (la syntaxe `{0..2}` est un caractère générique correspondant aux valeurs 0, 1 et 2) :

      ```sql theme={null}
      INSERT INTO nyc_taxi.trips_small
      SELECT
          trip_id,
          pickup_datetime,
          dropoff_datetime,
          pickup_longitude,
          pickup_latitude,
          dropoff_longitude,
          dropoff_latitude,
          passenger_count,
          trip_distance,
          fare_amount,
          extra,
          tip_amount,
          tolls_amount,
          total_amount,
          payment_type,
          pickup_ntaname,
          dropoff_ntaname
      FROM gcs(
          'https://storage.googleapis.com/clickhouse-public-datasets/nyc-taxi/trips_{0..2}.gz',
          'TabSeparatedWithNames'
      );
      ```
    </Tab>
  </Tabs>

  ## Exemples de requêtes

  Les requêtes suivantes sont exécutées sur l’échantillon décrit ci-dessus. Vous pouvez exécuter ces requêtes d’exemple sur l’ensemble du jeu de données dans [sql.clickhouse.com](https://sql.clickhouse.com/?query=U0VMRUNUIGNvdW50KCkgRlJPTSBueWNfdGF4aS50cmlwcw\&chart=eyJ0eXBlIjoibGluZSIsImNvbmZpZyI6eyJ0aXRsZSI6IlRlbXBlcmF0dXJlIGJ5IGNvdW50cnkgYW5kIHllYXIiLCJ4YXhpcyI6InllYXIiLCJ5YXhpcyI6ImNvdW50KCkiLCJzZXJpZXMiOiJDQVNUKHBhc3Nlbmdlcl9jb3VudCwgJ1N0cmluZycpIn19), en modifiant les requêtes ci-dessous pour utiliser la table `nyc_taxi.trips`.

  Voyons combien de lignes ont été insérées :

  <RunnableCode>
    ```sql theme={null}
    SELECT count()
    FROM nyc_taxi.trips_small;
    ```
  </RunnableCode>

  Chaque fichier TSV contient environ 1 M de lignes, et les trois fichiers contiennent 3 000 317 lignes. Examinons-en quelques-unes :

  <RunnableCode>
    ```sql theme={null}
    SELECT *
    FROM nyc_taxi.trips_small
    LIMIT 10;
    ```
  </RunnableCode>

  Notez qu’il existe des colonnes pour les dates de prise en charge et de dépose, les coordonnées géographiques, les informations tarifaires, les quartiers de New York, etc.

  Exécutons quelques requêtes. Cette requête affiche les 10 quartiers où les prises en charge sont les plus fréquentes :

  <RunnableCode>
    ```sql theme={null}
    SELECT
       pickup_ntaname,
       count(*) AS count
    FROM nyc_taxi.trips_small WHERE pickup_ntaname != ''
    GROUP BY pickup_ntaname
    ORDER BY count DESC
    LIMIT 10;
    ```
  </RunnableCode>

  Cette requête affiche le tarif moyen en fonction du nombre de passagers :

  ```sql runnable view='chart' chart_config='eyJ0eXBlIjoiYmFyIiwiY29uZmlnIjp7InhheGlzIjoicGFzc2VuZ2VyX2NvdW50IiwieWF4aXMiOiJhdmcodG90YWxfYW1vdW50KSIsInRpdGxlIjoiQXZlcmFnZSBmYXJlIGJ5IHBhc3NlbmdlciBjb3VudCJ9fQ' theme={null}
  SELECT
     passenger_count,
     avg(total_amount)
  FROM nyc_taxi.trips_small
  WHERE passenger_count < 10
  GROUP BY passenger_count;
  ```

  Voici la corrélation entre le nombre de passagers et la distance du trajet :

  ```sql runnable chart_config='eyJ0eXBlIjoiaG9yaXpvbnRhbCBiYXIiLCJjb25maWciOnsieGF4aXMiOiJwYXNzZW5nZXJfY291bnQiLCJ5YXhpcyI6ImRpc3RhbmNlIiwic2VyaWVzIjoiY291bnRyeSIsInRpdGxlIjoiQXZnIGZhcmUgYnkgcGFzc2VuZ2VyIGNvdW50In19' theme={null}
  SELECT
      passenger_count,
      avg(trip_distance) AS distance,
      count() AS c
  FROM nyc_taxi.trips_small
  GROUP BY passenger_count
  ORDER BY passenger_count ASC
  ```

  ## Téléchargement des partitions pré-générées

  <Note>
    Les étapes suivantes présentent le jeu de données d’origine, ainsi qu’une méthode pour charger des partitions préparées dans un environnement de serveur ClickHouse autogéré.
  </Note>

  Consultez [https://github.com/toddwschneider/nyc-taxi-data](https://github.com/toddwschneider/nyc-taxi-data) et [http://tech.marksblogg.com/billion-nyc-taxi-rides-redshift.html](http://tech.marksblogg.com/billion-nyc-taxi-rides-redshift.html) pour obtenir la description du jeu de données et les instructions de téléchargement.

  Le téléchargement représente environ 227 Go de données non compressées au format CSV. Il dure environ une heure sur une connexion à 1 Gbit (le téléchargement parallèle depuis s3.amazonaws.com exploite au moins la moitié d’un canal à 1 Gbit).
  Il est possible que certains fichiers ne soient pas téléchargés entièrement. Vérifiez la taille des fichiers et téléchargez à nouveau ceux qui vous paraissent suspects.

  ```bash theme={null}
  $ curl -O https://datasets.clickhouse.com/trips_mergetree/partitions/trips_mergetree.tar
  # Validate the checksum
  $ md5sum trips_mergetree.tar
  # Checksum should be equal to: f3b8d469b41d9a82da064ded7245d12c
  $ tar xvf trips_mergetree.tar -C /var/lib/clickhouse # path to ClickHouse data directory
  $ # check permissions of unpacked data, fix if required
  $ sudo service clickhouse-server restart
  $ clickhouse-client --query "select count(*) from datasets.trips_mergetree"
  ```

  <Info>
    Si vous exécutez les requêtes décrites ci-dessous, vous devez utiliser le nom complet de la table, `datasets.trips_mergetree`.
  </Info>

  ## Résultats sur un serveur unique

  Q1 :

  ```sql theme={null}
  SELECT cab_type, count(*) FROM trips_mergetree GROUP BY cab_type;
  ```

  0,490 seconde.

  Q2 :

  ```sql theme={null}
  SELECT passenger_count, avg(total_amount) FROM trips_mergetree GROUP BY passenger_count;
  ```

  1,224 seconde.

  Q3 :

  ```sql theme={null}
  SELECT passenger_count, toYear(pickup_date) AS year, count(*) FROM trips_mergetree GROUP BY passenger_count, year;
  ```

  2,104 secondes.

  Q4 :

  ```sql theme={null}
  SELECT passenger_count, toYear(pickup_date) AS year, round(trip_distance) AS distance, count(*)
  FROM trips_mergetree
  GROUP BY passenger_count, year, distance
  ORDER BY year, count(*) DESC;
  ```

  3,593 secondes.

  Le serveur suivant a été utilisé :

  Deux CPU Intel(R) Xeon(R) E5-2650 v2 @ 2.60GHz, 16 cœurs physiques au total, 128 Gio de RAM, 8 disques durs de 6 To en RAID-5 matériel

  Le temps d’exécution retenu est le meilleur de trois exécutions. Toutefois, dès la deuxième exécution, les requêtes lisent les données depuis le cache du système de fichiers. Aucune autre mise en cache n’intervient : les données sont lues et traitées à chaque exécution.

  Création d’une table sur trois serveurs :

  Sur chaque serveur :

  ```sql theme={null}
  CREATE TABLE default.trips_mergetree_third ( trip_id UInt32,  vendor_id Enum8('1' = 1, '2' = 2, 'CMT' = 3, 'VTS' = 4, 'DDS' = 5, 'B02512' = 10, 'B02598' = 11, 'B02617' = 12, 'B02682' = 13, 'B02764' = 14),  pickup_date Date,  pickup_datetime DateTime,  dropoff_date Date,  dropoff_datetime DateTime,  store_and_fwd_flag UInt8,  rate_code_id UInt8,  pickup_longitude Float64,  pickup_latitude Float64,  dropoff_longitude Float64,  dropoff_latitude Float64,  passenger_count UInt8,  trip_distance Float64,  fare_amount Float32,  extra Float32,  mta_tax Float32,  tip_amount Float32,  tolls_amount Float32,  ehail_fee Float32,  improvement_surcharge Float32,  total_amount Float32,  payment_type_ Enum8('UNK' = 0, 'CSH' = 1, 'CRE' = 2, 'NOC' = 3, 'DIS' = 4),  trip_type UInt8,  pickup FixedString(25),  dropoff FixedString(25),  cab_type Enum8('yellow' = 1, 'green' = 2, 'uber' = 3),  pickup_nyct2010_gid UInt8,  pickup_ctlabel Float32,  pickup_borocode UInt8,  pickup_boroname Enum8('' = 0, 'Manhattan' = 1, 'Bronx' = 2, 'Brooklyn' = 3, 'Queens' = 4, 'Staten Island' = 5),  pickup_ct2010 FixedString(6),  pickup_boroct2010 FixedString(7),  pickup_cdeligibil Enum8(' ' = 0, 'E' = 1, 'I' = 2),  pickup_ntacode FixedString(4),  pickup_ntaname Enum16('' = 0, 'Airport' = 1, 'Allerton-Pelham Gardens' = 2, 'Annadale-Huguenot-Prince\'s Bay-Eltingville' = 3, 'Arden Heights' = 4, 'Astoria' = 5, 'Auburndale' = 6, 'Baisley Park' = 7, 'Bath Beach' = 8, 'Battery Park City-Lower Manhattan' = 9, 'Bay Ridge' = 10, 'Bayside-Bayside Hills' = 11, 'Bedford' = 12, 'Bedford Park-Fordham North' = 13, 'Bellerose' = 14, 'Belmont' = 15, 'Bensonhurst East' = 16, 'Bensonhurst West' = 17, 'Borough Park' = 18, 'Breezy Point-Belle Harbor-Rockaway Park-Broad Channel' = 19, 'Briarwood-Jamaica Hills' = 20, 'Brighton Beach' = 21, 'Bronxdale' = 22, 'Brooklyn Heights-Cobble Hill' = 23, 'Brownsville' = 24, 'Bushwick North' = 25, 'Bushwick South' = 26, 'Cambria Heights' = 27, 'Canarsie' = 28, 'Carroll Gardens-Columbia Street-Red Hook' = 29, 'Central Harlem North-Polo Grounds' = 30, 'Central Harlem South' = 31, 'Charleston-Richmond Valley-Tottenville' = 32, 'Chinatown' = 33, 'Claremont-Bathgate' = 34, 'Clinton' = 35, 'Clinton Hill' = 36, 'Co-op City' = 37, 'College Point' = 38, 'Corona' = 39, 'Crotona Park East' = 40, 'Crown Heights North' = 41, 'Crown Heights South' = 42, 'Cypress Hills-City Line' = 43, 'DUMBO-Vinegar Hill-Downtown Brooklyn-Boerum Hill' = 44, 'Douglas Manor-Douglaston-Little Neck' = 45, 'Dyker Heights' = 46, 'East Concourse-Concourse Village' = 47, 'East Elmhurst' = 48, 'East Flatbush-Farragut' = 49, 'East Flushing' = 50, 'East Harlem North' = 51, 'East Harlem South' = 52, 'East New York' = 53, 'East New York (Pennsylvania Ave)' = 54, 'East Tremont' = 55, 'East Village' = 56, 'East Williamsburg' = 57, 'Eastchester-Edenwald-Baychester' = 58, 'Elmhurst' = 59, 'Elmhurst-Maspeth' = 60, 'Erasmus' = 61, 'Far Rockaway-Bayswater' = 62, 'Flatbush' = 63, 'Flatlands' = 64, 'Flushing' = 65, 'Fordham South' = 66, 'Forest Hills' = 67, 'Fort Greene' = 68, 'Fresh Meadows-Utopia' = 69, 'Ft. Totten-Bay Terrace-Clearview' = 70, 'Georgetown-Marine Park-Bergen Beach-Mill Basin' = 71, 'Glen Oaks-Floral Park-New Hyde Park' = 72, 'Glendale' = 73, 'Gramercy' = 74, 'Grasmere-Arrochar-Ft. Wadsworth' = 75, 'Gravesend' = 76, 'Great Kills' = 77, 'Greenpoint' = 78, 'Grymes Hill-Clifton-Fox Hills' = 79, 'Hamilton Heights' = 80, 'Hammels-Arverne-Edgemere' = 81, 'Highbridge' = 82, 'Hollis' = 83, 'Homecrest' = 84, 'Hudson Yards-Chelsea-Flatiron-Union Square' = 85, 'Hunters Point-Sunnyside-West Maspeth' = 86, 'Hunts Point' = 87, 'Jackson Heights' = 88, 'Jamaica' = 89, 'Jamaica Estates-Holliswood' = 90, 'Kensington-Ocean Parkway' = 91, 'Kew Gardens' = 92, 'Kew Gardens Hills' = 93, 'Kingsbridge Heights' = 94, 'Laurelton' = 95, 'Lenox Hill-Roosevelt Island' = 96, 'Lincoln Square' = 97, 'Lindenwood-Howard Beach' = 98, 'Longwood' = 99, 'Lower East Side' = 100, 'Madison' = 101, 'Manhattanville' = 102, 'Marble Hill-Inwood' = 103, 'Mariner\'s Harbor-Arlington-Port Ivory-Graniteville' = 104, 'Maspeth' = 105, 'Melrose South-Mott Haven North' = 106, 'Middle Village' = 107, 'Midtown-Midtown South' = 108, 'Midwood' = 109, 'Morningside Heights' = 110, 'Morrisania-Melrose' = 111, 'Mott Haven-Port Morris' = 112, 'Mount Hope' = 113, 'Murray Hill' = 114, 'Murray Hill-Kips Bay' = 115, 'New Brighton-Silver Lake' = 116, 'New Dorp-Midland Beach' = 117, 'New Springville-Bloomfield-Travis' = 118, 'North Corona' = 119, 'North Riverdale-Fieldston-Riverdale' = 120, 'North Side-South Side' = 121, 'Norwood' = 122, 'Oakland Gardens' = 123, 'Oakwood-Oakwood Beach' = 124, 'Ocean Hill' = 125, 'Ocean Parkway South' = 126, 'Old Astoria' = 127, 'Old Town-Dongan Hills-South Beach' = 128, 'Ozone Park' = 129, 'Park Slope-Gowanus' = 130, 'Parkchester' = 131, 'Pelham Bay-Country Club-City Island' = 132, 'Pelham Parkway' = 133, 'Pomonok-Flushing Heights-Hillcrest' = 134, 'Port Richmond' = 135, 'Prospect Heights' = 136, 'Prospect Lefferts Gardens-Wingate' = 137, 'Queens Village' = 138, 'Queensboro Hill' = 139, 'Queensbridge-Ravenswood-Long Island City' = 140, 'Rego Park' = 141, 'Richmond Hill' = 142, 'Ridgewood' = 143, 'Rikers Island' = 144, 'Rosedale' = 145, 'Rossville-Woodrow' = 146, 'Rugby-Remsen Village' = 147, 'Schuylerville-Throgs Neck-Edgewater Park' = 148, 'Seagate-Coney Island' = 149, 'Sheepshead Bay-Gerritsen Beach-Manhattan Beach' = 150, 'SoHo-TriBeCa-Civic Center-Little Italy' = 151, 'Soundview-Bruckner' = 152, 'Soundview-Castle Hill-Clason Point-Harding Park' = 153, 'South Jamaica' = 154, 'South Ozone Park' = 155, 'Springfield Gardens North' = 156, 'Springfield Gardens South-Brookville' = 157, 'Spuyten Duyvil-Kingsbridge' = 158, 'St. Albans' = 159, 'Stapleton-Rosebank' = 160, 'Starrett City' = 161, 'Steinway' = 162, 'Stuyvesant Heights' = 163, 'Stuyvesant Town-Cooper Village' = 164, 'Sunset Park East' = 165, 'Sunset Park West' = 166, 'Todt Hill-Emerson Hill-Heartland Village-Lighthouse Hill' = 167, 'Turtle Bay-East Midtown' = 168, 'University Heights-Morris Heights' = 169, 'Upper East Side-Carnegie Hill' = 170, 'Upper West Side' = 171, 'Van Cortlandt Village' = 172, 'Van Nest-Morris Park-Westchester Square' = 173, 'Washington Heights North' = 174, 'Washington Heights South' = 175, 'West Brighton' = 176, 'West Concourse' = 177, 'West Farms-Bronx River' = 178, 'West New Brighton-New Brighton-St. George' = 179, 'West Village' = 180, 'Westchester-Unionport' = 181, 'Westerleigh' = 182, 'Whitestone' = 183, 'Williamsbridge-Olinville' = 184, 'Williamsburg' = 185, 'Windsor Terrace' = 186, 'Woodhaven' = 187, 'Woodlawn-Wakefield' = 188, 'Woodside' = 189, 'Yorkville' = 190, 'park-cemetery-etc-Bronx' = 191, 'park-cemetery-etc-Brooklyn' = 192, 'park-cemetery-etc-Manhattan' = 193, 'park-cemetery-etc-Queens' = 194, 'park-cemetery-etc-Staten Island' = 195),  pickup_puma UInt16,  dropoff_nyct2010_gid UInt8,  dropoff_ctlabel Float32,  dropoff_borocode UInt8,  dropoff_boroname Enum8('' = 0, 'Manhattan' = 1, 'Bronx' = 2, 'Brooklyn' = 3, 'Queens' = 4, 'Staten Island' = 5),  dropoff_ct2010 FixedString(6),  dropoff_boroct2010 FixedString(7),  dropoff_cdeligibil Enum8(' ' = 0, 'E' = 1, 'I' = 2),  dropoff_ntacode FixedString(4),  dropoff_ntaname Enum16('' = 0, 'Airport' = 1, 'Allerton-Pelham Gardens' = 2, 'Annadale-Huguenot-Prince\'s Bay-Eltingville' = 3, 'Arden Heights' = 4, 'Astoria' = 5, 'Auburndale' = 6, 'Baisley Park' = 7, 'Bath Beach' = 8, 'Battery Park City-Lower Manhattan' = 9, 'Bay Ridge' = 10, 'Bayside-Bayside Hills' = 11, 'Bedford' = 12, 'Bedford Park-Fordham North' = 13, 'Bellerose' = 14, 'Belmont' = 15, 'Bensonhurst East' = 16, 'Bensonhurst West' = 17, 'Borough Park' = 18, 'Breezy Point-Belle Harbor-Rockaway Park-Broad Channel' = 19, 'Briarwood-Jamaica Hills' = 20, 'Brighton Beach' = 21, 'Bronxdale' = 22, 'Brooklyn Heights-Cobble Hill' = 23, 'Brownsville' = 24, 'Bushwick North' = 25, 'Bushwick South' = 26, 'Cambria Heights' = 27, 'Canarsie' = 28, 'Carroll Gardens-Columbia Street-Red Hook' = 29, 'Central Harlem North-Polo Grounds' = 30, 'Central Harlem South' = 31, 'Charleston-Richmond Valley-Tottenville' = 32, 'Chinatown' = 33, 'Claremont-Bathgate' = 34, 'Clinton' = 35, 'Clinton Hill' = 36, 'Co-op City' = 37, 'College Point' = 38, 'Corona' = 39, 'Crotona Park East' = 40, 'Crown Heights North' = 41, 'Crown Heights South' = 42, 'Cypress Hills-City Line' = 43, 'DUMBO-Vinegar Hill-Downtown Brooklyn-Boerum Hill' = 44, 'Douglas Manor-Douglaston-Little Neck' = 45, 'Dyker Heights' = 46, 'East Concourse-Concourse Village' = 47, 'East Elmhurst' = 48, 'East Flatbush-Farragut' = 49, 'East Flushing' = 50, 'East Harlem North' = 51, 'East Harlem South' = 52, 'East New York' = 53, 'East New York (Pennsylvania Ave)' = 54, 'East Tremont' = 55, 'East Village' = 56, 'East Williamsburg' = 57, 'Eastchester-Edenwald-Baychester' = 58, 'Elmhurst' = 59, 'Elmhurst-Maspeth' = 60, 'Erasmus' = 61, 'Far Rockaway-Bayswater' = 62, 'Flatbush' = 63, 'Flatlands' = 64, 'Flushing' = 65, 'Fordham South' = 66, 'Forest Hills' = 67, 'Fort Greene' = 68, 'Fresh Meadows-Utopia' = 69, 'Ft. Totten-Bay Terrace-Clearview' = 70, 'Georgetown-Marine Park-Bergen Beach-Mill Basin' = 71, 'Glen Oaks-Floral Park-New Hyde Park' = 72, 'Glendale' = 73, 'Gramercy' = 74, 'Grasmere-Arrochar-Ft. Wadsworth' = 75, 'Gravesend' = 76, 'Great Kills' = 77, 'Greenpoint' = 78, 'Grymes Hill-Clifton-Fox Hills' = 79, 'Hamilton Heights' = 80, 'Hammels-Arverne-Edgemere' = 81, 'Highbridge' = 82, 'Hollis' = 83, 'Homecrest' = 84, 'Hudson Yards-Chelsea-Flatiron-Union Square' = 85, 'Hunters Point-Sunnyside-West Maspeth' = 86, 'Hunts Point' = 87, 'Jackson Heights' = 88, 'Jamaica' = 89, 'Jamaica Estates-Holliswood' = 90, 'Kensington-Ocean Parkway' = 91, 'Kew Gardens' = 92, 'Kew Gardens Hills' = 93, 'Kingsbridge Heights' = 94, 'Laurelton' = 95, 'Lenox Hill-Roosevelt Island' = 96, 'Lincoln Square' = 97, 'Lindenwood-Howard Beach' = 98, 'Longwood' = 99, 'Lower East Side' = 100, 'Madison' = 101, 'Manhattanville' = 102, 'Marble Hill-Inwood' = 103, 'Mariner\'s Harbor-Arlington-Port Ivory-Graniteville' = 104, 'Maspeth' = 105, 'Melrose South-Mott Haven North' = 106, 'Middle Village' = 107, 'Midtown-Midtown South' = 108, 'Midwood' = 109, 'Morningside Heights' = 110, 'Morrisania-Melrose' = 111, 'Mott Haven-Port Morris' = 112, 'Mount Hope' = 113, 'Murray Hill' = 114, 'Murray Hill-Kips Bay' = 115, 'New Brighton-Silver Lake' = 116, 'New Dorp-Midland Beach' = 117, 'New Springville-Bloomfield-Travis' = 118, 'North Corona' = 119, 'North Riverdale-Fieldston-Riverdale' = 120, 'North Side-South Side' = 121, 'Norwood' = 122, 'Oakland Gardens' = 123, 'Oakwood-Oakwood Beach' = 124, 'Ocean Hill' = 125, 'Ocean Parkway South' = 126, 'Old Astoria' = 127, 'Old Town-Dongan Hills-South Beach' = 128, 'Ozone Park' = 129, 'Park Slope-Gowanus' = 130, 'Parkchester' = 131, 'Pelham Bay-Country Club-City Island' = 132, 'Pelham Parkway' = 133, 'Pomonok-Flushing Heights-Hillcrest' = 134, 'Port Richmond' = 135, 'Prospect Heights' = 136, 'Prospect Lefferts Gardens-Wingate' = 137, 'Queens Village' = 138, 'Queensboro Hill' = 139, 'Queensbridge-Ravenswood-Long Island City' = 140, 'Rego Park' = 141, 'Richmond Hill' = 142, 'Ridgewood' = 143, 'Rikers Island' = 144, 'Rosedale' = 145, 'Rossville-Woodrow' = 146, 'Rugby-Remsen Village' = 147, 'Schuylerville-Throgs Neck-Edgewater Park' = 148, 'Seagate-Coney Island' = 149, 'Sheepshead Bay-Gerritsen Beach-Manhattan Beach' = 150, 'SoHo-TriBeCa-Civic Center-Little Italy' = 151, 'Soundview-Bruckner' = 152, 'Soundview-Castle Hill-Clason Point-Harding Park' = 153, 'South Jamaica' = 154, 'South Ozone Park' = 155, 'Springfield Gardens North' = 156, 'Springfield Gardens South-Brookville' = 157, 'Spuyten Duyvil-Kingsbridge' = 158, 'St. Albans' = 159, 'Stapleton-Rosebank' = 160, 'Starrett City' = 161, 'Steinway' = 162, 'Stuyvesant Heights' = 163, 'Stuyvesant Town-Cooper Village' = 164, 'Sunset Park East' = 165, 'Sunset Park West' = 166, 'Todt Hill-Emerson Hill-Heartland Village-Lighthouse Hill' = 167, 'Turtle Bay-East Midtown' = 168, 'University Heights-Morris Heights' = 169, 'Upper East Side-Carnegie Hill' = 170, 'Upper West Side' = 171, 'Van Cortlandt Village' = 172, 'Van Nest-Morris Park-Westchester Square' = 173, 'Washington Heights North' = 174, 'Washington Heights South' = 175, 'West Brighton' = 176, 'West Concourse' = 177, 'West Farms-Bronx River' = 178, 'West New Brighton-New Brighton-St. George' = 179, 'West Village' = 180, 'Westchester-Unionport' = 181, 'Westerleigh' = 182, 'Whitestone' = 183, 'Williamsbridge-Olinville' = 184, 'Williamsburg' = 185, 'Windsor Terrace' = 186, 'Woodhaven' = 187, 'Woodlawn-Wakefield' = 188, 'Woodside' = 189, 'Yorkville' = 190, 'park-cemetery-etc-Bronx' = 191, 'park-cemetery-etc-Brooklyn' = 192, 'park-cemetery-etc-Manhattan' = 193, 'park-cemetery-etc-Queens' = 194, 'park-cemetery-etc-Staten Island' = 195),  dropoff_puma UInt16) ENGINE = MergeTree(pickup_date, pickup_datetime, 8192);
  ```

  Sur le serveur source :

  ```sql theme={null}
  CREATE TABLE trips_mergetree_x3 AS trips_mergetree_third ENGINE = Distributed(perftest, default, trips_mergetree_third, rand());
  ```

  La requête suivante redistribue les données :

  ```sql theme={null}
  INSERT INTO trips_mergetree_x3 SELECT * FROM trips_mergetree;
  ```

  Cette opération prend 2454 secondes.

  Sur trois serveurs :

  Q1: 0.212 secondes.
  Q2: 0.438 secondes.
  Q3: 0.733 secondes.
  Q4: 1.241 secondes.

  Rien de surprenant ici, puisque les performances des requêtes évoluent de façon linéaire.

  Nous disposons également des résultats obtenus sur un cluster de 140 serveurs :

  Q1 : 0,028 s
  Q2 : 0,043 s
  Q3 : 0,051 s
  Q4 : 0,072 s

  Dans ce cas, le temps de traitement des requêtes dépend avant tout de la latence réseau.
  Nous avons exécuté les requêtes depuis un client situé dans un autre datacenter que celui du cluster, ce qui a ajouté environ 20 ms de latence.

  ## Résumé

  | serveurs | Q1 | Q2 | Q3 | Q4 |
  | - | - | - | - | - |
  | 1, E5-2650v2 | 0.490 | 1.224 | 2.104 | 3.593 |
  | 3, E5-2650v2 | 0.212 | 0.438 | 0.733 | 1.241 |
  | 1, AWS c5n.4xlarge | 0.249 | 1.279 | 1.738 | 3.527 |
  | 1, AWS c5n.9xlarge | 0.130 | 0.584 | 0.777 | 1.811 |
  | 3, AWS c5n.9xlarge | 0.057 | 0.231 | 0.285 | 0.641 |
  | 140, E5-2650v2 | 0.028 | 0.043 | 0.051 | 0.072 |
</View>
