Question: I am working on a project to analyze card game tournaments and their participants. I have three tables: 'card_players', 'card_matches', and 'tournaments'. The 'card_players' table contains information about the players, the 'card_matches' table contains information about the matches played, and the 'tournaments' table contains information about the tournaments. I want to count how many tournaments have had at least one match where a player of type 'Pro' has participated. However, if both players in a match are of type 'Pro', I only want to count one tournament. Here's a simplified SQL of my database schema and some seed data. The problematic SQL query I used is provided below, and it counts both players in a match as separate tournaments, which is incorrect. I need a corrected query that counts each tournament only once if there is at least one 'Pro' player in any match.

SELECT count(*) FROM card_matches INNER JOIN card_players ON card_matches.player1_id = card_players.id OR card_matches.player2_id = card_players.id WHERE card_players.type = 'Pro'

CREATE TABLE card_players (id bigint NOT NULL, name text NOT NULL, type text NOT NULL, PRIMARY KEY(id));

CREATE TABLE card_matches (id bigint NOT NULL, tournament_id bigint NOT NULL, player1_id bigint NOT NULL, player2_id bigint NOT NULL, PRIMARY KEY(id));

CREATE TABLE tournaments (id bigint NOT NULL, name text NOT NULL, PRIMARY KEY(id));

INSERT INTO card_players (id, name, type) VALUES (1, 'Alice', 'Pro'), (2, 'Bob', 'Pro'), (3, 'Charlie', 'Amateur'), (4, 'David', 'Amateur');

INSERT INTO tournaments (id, name) VALUES (1, 'Grand Prix'), (2, 'Pro Tour'), (3, 'Super Bowl');

DROP TABLE card_players;
DROP TABLE card_matches;
DROP TABLE tournaments;


Question:
In the context of the European Football database, I have a set of matches where each match has a related home team, away team, season, and date. Most matches have been played several times during a season. In some cases, several matches took place at the same day and in the same league. I'd like to have one group (a match series) for all matches that have been played together during one season in one specific league, something like this:
| League ID | Season | Match IDs(a list of match ids) | Dates(a list of match dates)
I've been doing the heavy lifting in Python, but the code is quite slow and involves lots of queries. I've been wondering if any of you has ideas to combine the matches in one (or a few) queries?\nI have had no success doing this in Postgres so far.

WITH all_dates AS (SELECT league_id, season, ARRAY_AGG(DISTINCT id order by id) as match_ids, date AS match_date FROM match WHERE season IS NOT NULL AND league_id IS NOT NULL AND id is not null AND date IS NOT NULL GROUP BY league_id, season, date) SELECT DISTINCT league_id, season, match_ids, ARRAY_AGG(DISTINCT all_dates.match_date ORDER BY match_date) AS dates FROM all_dates GROUP BY season, league_id, match_ids order by league_id;


Question:
We have a dataset of card rulings in the 'ruling' table, where each ruling is associated with a unique card UUID. We need to transform this dataset into a format where each row represents a unique ruling id, and each column represents a different card UUID. The values in the cells should be the ruling text for that card. For each unique id, group all rules by card_uuid (e.g., card1, card2, card3). Assign a row number starting from 1 for each card_uuid grouping, and place the corresponding rule in the appropriate column. If a card_uuid doesn't have a rule for an id, insert NULL. The final output should have one row per id, with columns for each card_uuid and the corresponding rules or NULLs. The expected output should have ruling ids as rows and card UUIDs as columns, with ruling texts as the cell values. There are 3 uuids:'5f8287b1-5bb6-5f4c-ad17-316a40d5bb0c' as card1, '56f4935b-f6c5-59b9-88bf-9bcce20247ce' as card2, '6d268c95-c176-5766-9a46-c14f739aba1c' as card3.
 The expected output is like | id | card1 ruling | card2 ruling | card3 ruling |
However, when attempting to use the crosstab function, we encountered an error. Here is the problematic query we tried:

select * from crosstab ('select id, uuid, text from ruling order by 1,2') AS final_result(id int, uuid1 text, uuid2 text, uuid3 text);

CREATE TABLE ruling ( id integer, text text NULL, uuid text NULL);


Question: In the card_games database, there are two tables: cards_info and card_choice. The cards_info table contains information about various Magic: The Gathering cards, including their IDs and names. The card_choice table contains information about the customers' selections of different card types, including the card's ID and a JSONB field that lists the IDs of corresponding non-valid cards. These non-valid cards are the ones that a customer has deemed unsuitable or irrelevant to their selection, represented as an array of card IDs. The goal is to query the cards_info table and return the cards that are not listed in the non_valid_cards array for each card choice, based on the matching card_id.

SELECT c.*
FROM cards_info c
WHERE NOT EXISTS (
    SELECT 1
    FROM card_choice cc
    WHERE cc.card_id = c.id
    AND c.id = ANY (cc.non_valid_cards)
);

CREATE TABLE cards_info (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL
);
INSERT INTO cards_info (id, name)
VALUES
    (1, 'Ancestor`s Chosen'),
    (2, 'Angel of Mercy'),
    (3, 'Aven Cloudchaser');

CREATE TABLE card_choice (
    id SERIAL PRIMARY KEY,
    card_id INT REFERENCES cards_info(id),
    non_valid_cards JSONB
);
INSERT INTO card_choice (id, card_id, non_valid_cards)
VALUES
    (1, 1, '[1,3]'),
    (2, 3, '[1]'),
    (3, 2, '[2,3]');

DROP TABLE IF EXISTS cards_info; DROP TABLE IF EXISTS card_choice;


Question:
I have a certain hierarchy of data in the card_type table, where each row represents a type of cards with a unique identifier, a type name, and a reference to its parent card through the parent_uuid. The data is structured in a way that cards can be grouped under parent cards, forming a tree-like hierarchy.
I initially managed to create a recursive query that fetches the data, but the result isn't in the format I desire. The query correctly returns each card along with a list of parent uuids. However, instead of having the list of parent uuids, I would prefer to have a structured output where each parent card includes a list of its child cards (i.e., uuid and card_name).
For example, I want to convert the result into a structure where each parent card lists all its direct child cards grouped together, forming a tree-like structure. This would help me better visualize the hierarchy and relationships between the cards, with each parent card having an array of its children's uuid values.
Can you guide me on how to achieve this transformation using SQL?

WITH RECURSIVE nodes AS (
    SELECT
        uuid,
        card_name AS name,
        ARRAY[]::uuid[] AS parents
    FROM card_type
    WHERE parent_uuid IS NULL

    UNION ALL

    SELECT
        c.uuid,
        c.card_name AS name,
        nodes.parents || c.uuid
    FROM card_type c
    JOIN nodes ON nodes.uuid = c.parent_uuid
)
SELECT * FROM nodes;

CREATE TABLE card_type
(
    uuid uuid NOT NULL PRIMARY KEY,
    card_name character varying(32),
    parent_uuid uuid REFERENCES card_type(uuid)
);

INSERT INTO card_type (uuid, card_name, parent_uuid)
VALUES
('8a70180b-3644-4b17-af5f-93cbe0090cce', 'Creature', null),
('d9093660-241a-48f6-bf09-b6a8c6c7f12a', 'Human', '8a70180b-3644-4b17-af5f-93cbe0090cce'),
('376ae1cb-425d-44d2-b19a-19b6f1e86314', 'Cleric', 'd9093660-241a-48f6-bf09-b6a8c6c7f12a'),
('5d5f174a-5c8e-4d12-912f-8173e255e35a', 'Knight', 'd9093660-241a-48f6-bf09-b6a8c6c7f12a'),
('f79f5fa0-6eaf-465b-9b14-e3b49c5ac9ef', 'Enchantment', null),
('8aa95eda-7963-40ef-be44-076cdf06c5c1', 'Aura', 'f79f5fa0-6eaf-465b-9b14-e3b49c5ac9ef');


Question: In the context of the student_club database, we have two tables: `event` and `budget`. The `event` table contains information about various events, including their start dates and statuses. The `budget` table contains financial details related to these events, including the amount budgeted and the remaining budget. The user wants to know the average remaining budget and the number of events that are open or closed on a daily basis between '2020-01-01' and '2020-03-31'. The user attempted to write a query to achieve this, but it resulted in incorrect results or errors. Below is the problematic SQL statement the user used, followed by the corrected solution.

SELECT d.the_day AS "Date", COUNT(e.event_id) AS "Number of Events", AVG(b.remaining) AS "Avg Remaining Budget" FROM (SELECT ts::date AS the_day FROM generate_series (timestamp '2020-01-01', timestamp '2020-03-31', interval '1 day'::interval) ts) d LEFT JOIN "event" e ON e.event_date::date = d.the_day LEFT JOIN budget b ON b.link_to_event = e.event_id GROUP BY d.the_day ORDER BY d.the_day;


Question: I log the daily transactions of my bank accounts. Now I want to create a SQL statement to get the sum of transaction amounts for each month but separate columns for each year. I came up with the following SQL statement: sql SELECT LPAD(extract (month from trans.date)::text, 2, '0') as month, sum(trans.amount) as 1998 from trans WHERE trans.date >= '1998-01-01' and trans.date < '1999-01-01' group by month order by 1;  This results in only getting the values from 1998: | month | a1998 | | -------- | -------------- | | 1    | 100            | | 2   | 358            | | 3   | 495            | How could I change the SQL statement to get new columns for each year? Is this even possible?

SELECT LPAD(extract (month from trans.date)::text, 2, '0') as month, sum(trans.amount) as a1997 from trans WHERE trans.date >= '1997-01-01' and trans.date < '1998-01-01' group by month order by 1;


Question: I'm trying to run a query that will find circuits within a given distance of any of the selected locations. This is for a search result where users can select multiple locations to look around. My current approach is to use `ST_ClosestPoint` and pass in an array of `ST_Point` generated in PHP. Then I pass that string into `ST_Collect`. However, this doesn't work because it looks like `ST_ClosestPoint` doesn't like these mixed arguments. I have a `gist(coordinate::geography)` index on `circuits` which seems like it would be useful to use. What am I missing - is there a better way to do this, or is this a bad approach? Should I be performing the query each time with a different location?

SELECT * FROM circuits WHERE ST_DWithin(ST_SetSRID(ST_MakePoint(lng, lat), 4326)::geography, ST_ClosestPoint((ST_MakePoint(lng, lat), 4326)::geography, ST_Collect(Array[ST_SetSRID(ST_MakePoint(2.76083, 101.73800), 4326)::geography, ST_SetSRID(ST_MakePoint(26.03250, 50.51060), 4326)::geography])), 1000000, FALSE);


Question:
In the Formula 1 database, I have a results table currently containing around 400 million entries and the following query on it:

The query retrieves the top 10 result IDs for a specific driver or another driver, ordered by the result ID in descending order. The query works fine when filtering by a single driver ID, but it takes several minutes when filtering by two driver IDs, even if both drivers have a small number of results (e.g., 2 for the first driver and 57 for the second driver). The issue seems to be related to the combination of ORDER BY and LIMIT clauses.

SELECT resultid FROM results WHERE driverid = 3 OR driverid = 4 ORDER BY resultid DESC LIMIT 10


Question: In a multiplayer card game platform, when a player is misbehaving, their 'muted' status is set to true. Chat messages from muted players should be hidden from all other players except the muted player themselves to prevent them from noticing their muted status and creating new accounts. The platform uses PostgreSQL 14.2 and has the following tables: 'words_users', 'words_social', 'words_games', and 'words_chat'. The user has prepared a test case with two users, one of whom is muted, and a game where both users have exchanged messages. The user's current SQL function to retrieve chat messages does not filter out messages from muted players for other players. The user seeks to modify the SQL function to hide messages from muted players for everyone except the muted player themselves.

CREATE OR REPLACE FUNCTION words_get_chat(in_gid integer, in_social integer, in_sid text) RETURNS TABLE (out_mine integer, out_msg text) AS $func$ SELECT CASE WHEN c.uid = s.uid THEN 1 ELSE 0 END, c.msg FROM words_chat c JOIN words_games g USING (gid) JOIN words_social s ON s.uid IN (g.player1, g.player2) WHERE c.gid = in_gid AND s.social = in_social AND s.sid = in_sid ORDER BY c.CREATED ASC; $func$ LANGUAGE sql;

SELECT words_get_chat(10, 100, 'abc') AS nice_user;

CREATE TABLE words_users (uid SERIAL PRIMARY KEY, muted BOOLEAN NOT NULL DEFAULT false);

CREATE TABLE words_social (sid text NOT NULL CHECK (sid ~ '\S'), social integer NOT NULL CHECK (0 < social AND social <= 256), given text NOT NULL CHECK (given ~ '\S'), uid integer NOT NULL REFERENCES words_users ON DELETE CASCADE, PRIMARY KEY(sid, social));

CREATE TABLE words_games (gid SERIAL PRIMARY KEY, player1 integer REFERENCES words_users(uid) ON DELETE CASCADE NOT NULL CHECK (player1 <> player2), player2 integer REFERENCES words_users(uid) ON DELETE CASCADE);

CREATE TABLE words_chat (cid BIGSERIAL PRIMARY KEY, created timestamptz NOT NULL, gid integer NOT NULL REFERENCES words_games ON DELETE CASCADE, uid integer NOT NULL REFERENCES words_users ON DELETE CASCADE, msg text NOT NULL);

INSERT INTO words_users (uid, muted) VALUES (1, false), (2, true);

INSERT INTO words_social (sid, social, given, uid) VALUES ('abc', 100, 'Nice user', 1), ('def', 200, 'Bad user', 2);

INSERT INTO words_games (gid, player1, player2) VALUES (10, 1, 2);

INSERT INTO words_chat (gid, uid, created, msg) VALUES (10, 1, CURRENT_TIMESTAMP + INTERVAL '1 min', 'Hi how are you doing?'), (10, 1, CURRENT_TIMESTAMP + INTERVAL '2 min', 'I am a nice user'), (10, 2, CURRENT_TIMESTAMP + INTERVAL '3 min', 'F*** ***!!'), (10, 2, CURRENT_TIMESTAMP + INTERVAL '4 min', 'I am a bad user'), (10, 1, CURRENT_TIMESTAMP + INTERVAL '5 min', 'Are you there??');

DROP TABLE words_users;
DROP TABLE words_social;
DROP TABLE words_games;
DROP TABLE words_chat;


Question: A Formula 1 team is analyzing the performance and financial impact of their drivers in various races. They need to calculate the total earnings for each driver based on their points and a bonus system. The bonus is calculated as follows: if the total earnings exceed 5000 points, a 20% bonus is applied; if the total earnings exceed 3000 points, a 15% bonus is applied. The team wants to avoid repeating the calculation of total earnings multiple times in their query.

SELECT driverid, points, (points * 100) as earnings, CASE WHEN (points * 100) > 5000 THEN (points * 100) * 0.2 WHEN (points * 100) > 3000 THEN (points * 100) * 0.15 ELSE null END AS bonus FROM driverstandings


Question:
The data returned as `dataset` in the CTE below represents the number of times team attributes were recorded for each date within a specific time frame. The dataset looks like this:

| date | rows_added |
How can I incorporate a count of the duplicate records, by date, in the following CTE? If I was going to only count the duplicate dates I would use the following but I can't incorporate it into the CTE above:

sql
SELECT date, COUNT(date)
FROM dataset
GROUP BY date
HAVING COUNT(date) >1

Desired output given the example above:

|total_days_in_result_set | total_days_w_distinct_record_counts | toal_days_w_duplicate_record_counts | duplicate_dates |

with dataset as (
  SELECT
      date,
      COUNT(*) as rows_added
  FROM
      team_attributes
  WHERE
      date between '2010-01-01 00:00:00'
      AND '2015-12-31 00:00:00'
  GROUP BY
      date
 )
SELECT
    COUNT(*) as total_days_in_result_set,
    COUNT(DISTINCT rows_added) as total_days_w_distinct_record_counts,
    COUNT(*) - COUNT(DISTINCT rows_added) as toal_days_w_duplicate_record_counts
FROM dataset


Question:
I'm trying to handle an array of counters column in Postgres for a card collection tracking system. For example, let's say I have this table:

| card_name | counters | where counters is a list of numbers like [1,2,3,4] and now I'm adding 2 values [2,0,2,1] and [1,3,1,0]).

I expect the query to sum between the 2 counters vectors on conflict ([1,3,1,0] + [2,0,2,1] = [3,3,3,1]).

The expected counters are [3,3,3,1]. I had a try but it didn't seem to work, what am I missing?

insert into card_counters (card_name, counters) values ('Lightning Bolt', array[2,0,2,1]) on conflict (card_name) do update set counters = array_agg(unnest(card_counters.counters) + unnest(array[2,0,2,1]))

CREATE TABLE card_counters (card_name text PRIMARY KEY, counters integer[]);

INSERT INTO card_counters (card_name, counters) VALUES ('Lightning Bolt', array[1,3,1,0]);


Question: In the erolp database, we have a table named sales_transactions that records the transaction details of a financial application. Each transaction has an id, a transaction amount, an event multiplier, and a desired result which is the accumulation of the transaction amounts and previous results, multiplied by the event multiplier. The user is trying to calculate the 'result_good' column based on the recursive relationship between the rows. The user has attempted various methods including arrays, lateral views, recursive views, custom functions, and variables but has not been successful. The expected output is | id | result | and result is retained to 6 decimal places

select id, round(prev + event * sum(prev) over(order by id range between unbounded preceding and 1 preceding)::numeric(10, 6), 6) as not_quite_my_tempo from (select *, event*sum(trx) over(order by id) as prev from sales_transactions) t order by id

CREATE TABLE sales_transactions (id int, trx int, event numeric(10,5), result_good numeric(10,6));

DROP TABLE IF EXISTS sales_transactions;


Question:
We have a dataset of schools in California, and we are interested in analyzing the distribution of schools based on their funding type. We have created a Common Table Expression (CTE) named cte_funding_count that contains the count of schools for each funding type. The CTE looks like this:
| fundingtype | count |
From this CTE, we want to calculate the percentage of the count compared to the sum of the count as a new third column, and we want the percentage without decimals. However, when we do that, we get a sum of the percent column that is not exactly 100 due to rounding. How do we avoid this?

WITH cte_funding_count AS (SELECT fundingtype, COUNT(*) AS count FROM schools GROUP BY fundingtype) SELECT fundingtype, count, ROUND(count*100/(SELECT SUM(count) FROM cte_funding_count),0) AS percent FROM cte_funding_count


Question: In the thrombosis_prediction database, I need to find the first laboratory test activity for each patient that occurred between the patient's first recorded data date and the date of their first hospital admission. The laboratory table contains the test dates, and the patient table contains the first recorded data date and the admission date. The patient's ID is the common key between the two tables. I want to retrieve the date of the first laboratory test activity and patient id for patients with sex F.

SELECT lab.date AS firstActivity, pat.id FROM patient pat JOIN laboratory lab ON lab.id = pat.id AND lab.date <= pat.description AND lab.date > pat."First Date" WHERE pat.sex='F' order by pat.id


Question: In the card_games database, I have a table  that contains an id column (id) and another column that contains an array of strings. I have a select query that gets me an id that matches cards.id, as well as an array of values. I would like to now remove from the keywords array, any strings that are contained in the RemoveKeywords array that match on id. If the array is empty, the output return is []. For example, given cards (1, ['test']) and selectquery (1, ['test']), the output is (1, []) but not none

select id, array_agg(elem) from cards, unnest(string_to_array(cards.keywords, ',')::text[]) elem where elem <> all(SELECT unnest(sq.RemoveKeywords) from SelectQuery sq) and id in (61, 65) group by id order by id;

CREATE TABLE SelectQuery (id bigint, RemoveKeywords text[]);

INSERT INTO SelectQuery (id, RemoveKeywords) VALUES (65, ARRAY['Flying']), (61, ARRAY['Landwalk']);


Question: We have a table named "Experiment" in the toxicology database that records the hourly measurements of various chemical reactions over a period of 2009 to the present. Each record includes a timestamp and the concentration levels of different chemicals such as Chlorine and Carbon. We need to aggregate (average) the concentration levels across different intervals from specific timepoints, for example, data from 2021-01-07T00:00:00.000Z for one year at 7 day intervals, or 3 months at 1 day interval or 7 days at 1h interval etc. The date_trunc() function partly solves this, but rounds the weeks to the nearest Monday, e.g. the following query returns the first time series interval as 2021-01-04 with an incorrect count:

SELECT date_trunc('week', "TIMESTAMP") AS week, count(*), AVG("CL") AS cl, AVG("C") AS c FROM "Experiment" WHERE "TIMESTAMP" >= '2021-01-07T00:00:00.000Z' AND "TIMESTAMP" <= '2022-01-06T23:59:59.999Z' GROUP BY week ORDER BY week ASC

CREATE TABLE "Experiment" ("TIMESTAMP" timestamp NOT NULL, "CL" numeric NOT NULL, "C" numeric NOT NULL);