sql >> Database >  >> RDS >> PostgreSQL

Datums, tijden en intervallen weergeven in PostgreSQL

PostgreSQL wordt geleverd met een heleboel ingebouwde datum- en tijdgerelateerde gegevenstypen. Waarom zou je ze over strings of gehele getallen moeten gebruiken? Waar moet je op letten bij het gebruik ervan? Lees voor meer informatie over hoe u effectief kunt werken met deze datatypes in Postgres.

Heel veel soorten

De SQL-standaard, de ISO 8601-standaard, de ingebouwde catalogus van PostgreSQL en achterwaartse compatibiliteit definiëren samen een overvloed aan overlappende, aanpasbare datum/tijd-gerelateerde gegevenstypen en conventies die op zijn best verwarrend zijn. Deze verwarring vloeit meestal over in de databasestuurprogrammacode, toepassingscode, SQL-routines en resulteert in subtiele bugs die moeilijk te debuggen zijn.

Aan de andere kant vereenvoudigt het gebruik van native ingebouwde typen SQL-instructies en maakt ze veel gemakkelijker te lezen en te schrijven, en bijgevolg minder foutgevoelig. Het gebruik van bijvoorbeeld gehele getallen (aantal seconden sinds het tijdperk) om tijd weer te geven, resulteert in onpraktische SQL-expressies en meer applicatiecode.

De voordelen van native typen maken het de moeite waard om een ​​set niet-zo-pijnlijke regels te definiëren en deze af te dwingen in de toepassing en ops-codebasis. Hier is zo'n set, die redelijke standaardwaarden zou moeten bieden en een redelijk uitgangspunt voor verdere aanpassing indien nodig.

Typen

Gebruik alleen de volgende 3 typen (hoewel er veel beschikbaar zijn):

  • datum - een specifieke datum, zonder tijd
  • timestamptz - een specifieke datum en tijd met een resolutie van microseconden
  • interval - een tijdsinterval met een resolutie van microseconden

Deze drie typen samen zouden de meeste toepassingen van toepassingen moeten ondersteunen. Als je geen specifieke behoeften hebt (zoals het besparen van opslagruimte), wordt het ten zeerste aanbevolen om je aan alleen deze typen te houden.

De datum staat voor een datum zonder tijd, en is in de praktijk best handig (zie onderstaande voorbeelden). Het type tijdstempel is de variant die de tijdzone-informatie bevat – zonder de tijdzone-informatie zijn er gewoon te veel variabelen die de interpretatie en extractie van de waarde kunnen beïnvloeden.Ten slotte, het interval vertegenwoordigt tijdsintervallen van slechts een microseconde tot miljoenen jaren.

Literale tekenreeksen

Gebruik alleen de volgende letterlijke representaties en gebruik de cast-operator om breedsprakigheid te verminderen zonder de leesbaarheid op te offeren:

  • '2012-12-25'::date - ISO 8601
  • '2012-12-25 13:04:05.123-08:00'::timestamptz - ISO 8601
  • '1 month 3 days'::interval - Traditioneel Postgres-formaat voor intervalinvoer

Als u de tijdzone weglaat, bent u overgeleverd aan de tijdzone-instelling van de Postgres-server, de TimeZone-configuratie die kan worden ingesteld op databaseniveau, sessieniveau, rolniveau of in de verbindingsreeks, de tijdzone-instelling van de clientcomputer en meer van dergelijke factoren.

Converteer tijdens het opvragen vanuit de applicatiecode intervaltypen naar een geschikte eenheid (zoals dagen of seconden) met behulp van het extract functie en lees de waarde in als een geheel getal of reële waarde.

Configuratie en andere instellingen

  • Wijzig de standaardinstellingen voor de GUC-configuratie DateStyle niet ,TimeZone en lc_time .
  • Stel of gebruik de omgevingsvariabelen niet PGDATESTYLE en PGTZ .
  • Gebruik SET [SESSION|LOCAL] TIME ZONE ... .
  • Als je kunt, stel dan de systeemtijdzone in op UTC op de machine waarop de Postgres-server draait, evenals op alle machines waarop de applicatiecode wordt uitgevoerd die hiermee verbinding maakt.
  • Controleer of uw databasestuurprogramma (zoals een JDBC-connector of een Godatabase/sql-stuurprogramma) zich verstandig gedraagt ​​terwijl de client op de ene tijdzone draait en de server op een andere. Zorg ervoor dat het correct werkt wanneer een geldige niet-UTC TimeZone parameter is opgenomen in de verbindingsreeks.

Houd er ten slotte rekening mee dat dit allemaal slechts richtlijnen zijn en kunnen worden aangepast aan uw behoeften, maar zorg ervoor dat u eerst de implicaties ervan onderzoekt.

Native typen en operators

Dus hoe helpt het gebruik van native typen precies bij het vereenvoudigen van SQL-code? Hier zijn enkele voorbeelden.

Datumtype

Waarden van de datum type kan worden afgetrokken om het interval . te geven tussen hen. U kunt ook een geheel aantal dagen toevoegen aan een deeltjesdatum, of een interval toevoegen aan een datum om een ​​timestamptz te geven :

-- 10 days from now (outputs 2020-07-26)
SELECT now()::date + 10;
 
-- 10 days from now (outputs 2020-07-26 04:44:30.568847+00)
SELECT now() + '10 days'::interval;

-- days till christmas (outputs 161 days 14:06:26.759466)
SELECT '2020-12-25'::date - now();

-- the 10 longest courses
  SELECT name, end_date - start_date AS duration
    FROM courses
ORDER BY end_date - start_date DESC
   LIMIT 10;

De waarden van deze typen zijn vergelijkbaar, daarom zou je de laatste query kunnen bestellen voor end_date - start_date , die een type interval . heeft . Hier is nog een voorbeeld:

-- certificates expiring within the next 7 days
SELECT name
  FROM certificates
 WHERE expiry_date BETWEEN now() AND now() + '7 days'::interval;

Tijdstempeltype

Waarden van het type timestamptz kan ook worden afgetrokken (om een ​​interval te geven ),toegevoegd (aan een interval om nog een timestamptz te geven ) en vergeleken.

-- difference of timestamps gives an interval
SELECT password_last_modified - created_at AS password_age
  FROM users;

-- can also use the age() function
SELECT age(password_last_modified, created_at) AS password_age
  FROM users;

Houd er rekening mee dat er 3 verschillende ingebouwde functies zijn die verschillende "huidige tijdstempel" -waarden retourneren. Ze geven eigenlijk verschillende dingen terug:

-- transaction_timestamp() returns the timestampsz of the start of current transaction
-- outputs 2020-07-16 05:09:32.677409+00
SELECT transaction_timestamp();

-- statement_timestamp() returns the timestamptz of the start of the current statement
SELECT statement_timestamp();

-- clock_timestamp() returns the timestamptz of the system clock
SELECT clock_timestamp();

Er zijn ook aliassen voor deze functies:

-- now() actually returns the start of the current transaction, which means it
-- does not change during the transaction
SELECT now(), transaction_timestamp();

-- transaction timestamp is also returned by these keyword-style constructs
SELECT CURRENT_DATE, CURRENT_TIMESTAMP, transaction_timestamp();

Intervaltypen

Intervalgetypte waarden kunnen worden gebruikt als kolomgegevenstypes, kunnen met elkaar worden vergeleken en kunnen worden toegevoegd aan (en afgetrokken van) tijdstempels en datums. Hier zijn enkele voorbeelden:

-- interval-typed values can be stored and compared 
  SELECT num
    FROM passports
   WHERE valid_for > '10 years'::interval
ORDER BY valid_for DESC;

-- you can multiply them by numbers (outputs 4 years)
SELECT 4 * '1 year'::interval;

-- you can divide them by numbers (outputs 3 mons)
SELECT '1 year'::interval / 4;

-- you can add and subtract them (outputs 1 year 1 mon 6 days)
SELECT '1 year'::interval + '1.2 months'::interval;

Andere functies en constructies

PostgreSQL wordt ook geleverd met een paar handige functies en constructies die kunnen worden gebruikt om dit soort waarden te manipuleren.

Extract

De extract-functie kan worden gebruikt om een ​​gespecificeerd deel uit de gegeven waarde te halen, zoals de maand van een datum. De volledige lijst met onderdelen die kunnen worden geëxtraheerd, wordt hier gedocumenteerd. Hier zijn een paar nuttige en niet voor de hand liggende voorbeelden:

-- years from an interval (outputs 2)
SELECT extract(YEARS FROM '1.5 years 6 months'::interval);

-- day of the week (0=Sun .. 6=Sat) from timestamp (outputs 4)
SELECT extract(DOW FROM now());

-- day of the week (1=Mon .. 7=Sun) from timestamp (outputs 4)
SELECT extract(ISODOW FROM now());

-- convert interval to seconds (outputs 86400)
SELECT extract(EPOCH FROM '1 day'::interval);

Het laatste voorbeeld is met name handig bij query's die door toepassingen worden uitgevoerd, omdat het voor toepassingen gemakkelijker kan zijn om een ​​interval als een drijvende-kommawaarde van het aantal seconden/minuten/dagen/etc. te verwerken.

Tijdzoneconversie

Er is ook een handige functie om een ​​timestamptz uit te drukken in een andere tijdzone. Normaal gesproken zou dit in de applicatiecode worden gedaan - het is op die manier gemakkelijker te testen en vermindert de afhankelijkheid van de tijdzonedatabase waarnaar de Postgresserver zal verwijzen. Toch kan het soms nuttig zijn:

-- convert timestamps to a different time zone
SELECT timezone('Europe/Helsinki', now());

-- same as before, but this one is a SQL standard
SELECT now() AT TIME ZONE 'Europe/Helsinki';

Converteren van en naar tekst

De functie to_char (docs)kan datums, tijdstempels en intervallen naar tekst converteren op basis van een opmaakreeks – het Postgres-equivalent van de klassieke C-functie strftime .

-- outputs Thu, 16th July
SELECT to_char(now(), 'Dy, DDth Month');

-- outputs 01 06 00 12 00 00
SELECT to_char('1.5 years'::interval, 'YY MM DD HH MI SS');

Gebruik voor het converteren van tekst naar datums to_date , en voor het converteren van tekst naar tijdstempels gebruik je to_timestamp . Merk op dat als je de formulieren gebruikt die aan het begin van dit bericht zijn vermeld, je in plaats daarvan gewoon de cast-operators kunt gebruiken.

-- outputs 2000-12-25 15:42:50+00
SELECT to_timestamp('2000.12.25.15.42.50', 'YYYY.MM.DD.HH24.MI.SS');

-- outputs 2000-12-25
SELECT to_date('2000.12.25.15.42.50', 'YYYY.MM.DD');

Zie de documenten voor de volledige lijst met tekenreekspatronen voor opmaak.

Het is het beste om deze functies te gebruiken voor eenvoudige gevallen. Voor meer gecompliceerde ontleding of opmaak, is het beter om te vertrouwen op applicatiecode, die (aantoonbaar) beter door eenheden kan worden getest.

Interfacing met applicatiecode

Het is soms niet handig om datum-/timestamptz-/intervalwaarden van en naar de toepassingscode door te geven, vooral wanneer afhankelijke parameters worden gebruikt. Het is bijvoorbeeld meestal handiger om een ​​interval door te geven als een geheel aantal dagen (of uren of minuten) in plaats van in een tekenreeksindeling. Het is ook gemakkelijker om in een interval te lezen als een geheel getal/drijvende komma aantal dagen (of uren, of minuten enz.).

De make_interval functie kan worden gebruikt om een ​​intervalwaarde te maken van een geheel aantal componentwaarden (zie de documenten hier). De to_timestamp functie die we eerder zagen, heeft een andere vorm die atimestamptz-waarde van Unix-tijdperk kan creëren.

-- pass the interval as number of days from the application code
SELECT name FROM courses WHERE duration <= make_interval(days => $1);

-- pass timestamptz as unix epoch (number of seconds from 1-Jan-1970)
SELECT id FROM events WHERE logged_at >= to_timestamp($1);

-- return interval as number of days (with a fractional part)
SELECT extract(EPOCH FROM duration) / 60 / 60 / 24;

  1. SQL niet gelijk aan (!=) Operator voor beginners

  2. Sla de uitvoer van de geselecteerde query op in één array in postgres

  3. Beperking wijzigen?

  4. ORACLE Connect by clausule equivalent in SQL Server