Postgresql unique index: only one date for one foreign key - postgresql

I have a table "foo":
ID PRODUCT_ID END_DATE
------------------------------
1 1 NULL
2 1 2022-02-02
3 1 2022-01-06 - This date could not be exists
4 2 NULL
5 2 2022-01-23
6 3 NULL
7 3 NULL
How can i make unique index for one product whith the same id only one date can exist?

Create a conditional unique index:
CREATE UNIQUE INDEX u_productid_end_date_not_null
ON foo(product_Id)
WHERE end_date IS NOT NULL; -- this one will do the trick

Related

How to add SQL Constraint to column to check that only 1 row is set to true when the table is grouped by 3 columns?

What I would like to do is to add a constraint to a table that can only have the column named primary (which is a bool) set to true only once for a row grouped by 3 columns user_id, seat_id, account_id & where user_challenge status is IN_PROGRESS.
For example this table named users would pass the check constraint:
User_id
seat_id
account_id
status
primary
5
4
3
IN_PROGRESS
false
5
4
3
IN_PROGRESS
true
5
4
3
IN_PROGRESS
false
1
2
7
IN_PROGRESS
true
1
2
7
IN_PROGRESS
false
1
2
7
IN_PROGRESS
false
ALTER TABLE users
ADD CONSTRAINT users_primary_group
CHECK "primary"=true (user_id, seat_id)
where status = "IN_PROGRESS"
select challenge_id, seat_id, account_id, status
from users
where status = 'IN_PROGRESS'
group by challenge_id, seat_id, account_id, status
That's a conditional unique constraint:
CREATE UNIQUE INDEX u_user_user_id_seat_id_account_id ON users(
columns user_id
, seat_id
, account_id
)
WHERE status = 'IN_PROGRESS'
AND primary = TRUE;

How can i get all rows from two tables Postgres

I have a problem with JOIN of two tables.
CREATE table appointment(
idappointment serial primary key,
idday int references days(idday),
worktime text
);
create table booking(
idbooking serial,
idappointment int references appointment(idappointment),
date date,
primary key(idappointment)
);
appointment
idappointment
idday
worktime
1
1
07:00-08:00
2
1
08:00-09:00
3
1
09:00-10:00
4
2
09:00-10:00
booking
idbooking
idappointment
date
1
1
2021-08-22
1
2
2021-08-2
And I want :
idbooking
idappointment
date
idbooking
idappointment
date
1
1
07:00-08:00
null
null
null
2
1
08:00-09:00
null
null
null
3
1
09:00-10:00
null
null
null
4
2
09:00-10:00
null
null
null
null
null
null
1
1
2021-08-22
null
null
null
1
2
2021-08-2
1
1
07:00-08:00
1
1
2021-08-22
2
1
08:00-09:00
1
2
2021-08-2
How can I get it ?

T_SQL counting particular values in one row with multiple columns

I have little problem with counting cells with particular value in one row in MSSMS.
Table looks like
ID
Month
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
11
12
13
14
15
16
...
31
5000
1
null
null
1
1
null
1
1
null
null
2
2
2
2
2
null
null
3
3
3
3
3
null
...
1
I need to count how many cells in one row have value for example 1. In this case it would be 5.
Data represents worker shifts in a month. Be aware that there is a column named month (FK with values 1-12), i don't want to count that in a result.
Column ID is ALWAYS 4 digit number.
Possibility is to use count(case when) but in examples there are only two or three columns not 31. Statement will be very long. Is there any other option to count it?
Thanks for any advices.
I'm going to strongly suggest that you abandon your current table design, and instead store one day per month, per record, not column. That is, use this design:
ID | Date | Value
5000 | 2021-01-01 | NULL
5000 | 2021-01-02 | NULL
5000 | 2021-01-03 | 1
5000 | 2021-01-04 | 1
5000 | 2021-01-05 | NULL
...
5000 | 2021-01-31 | 5
Then use this query:
SELECT
ID,
CONVERT(varchar(7), Date, 120),
COUNT(CASE WHEN Value = 1 THEN 1 END) AS one_cnt
FROM yourTable
GROUP BY
ID,
CONVERT(varchar(7), Date, 120);

Remove duplicates based on only 1 column

My data is in the following format:
rep_id user_id other non-duplicated data
1 1 ...
1 2 ...
2 3 ...
3 4 ...
3 5 ...
I am trying to achieve a column for deduped_rep with 0/1 such that only first rep id across the associated users has a 1 and rest have 0.
Expected result:
rep_id user_id deduped_rep
1 1 1
1 2 0
2 3 1
3 4 1
3 5 0
For reference, in Excel, I would use the following formula:
IF(SUMPRODUCT(($A$2:$A2=A2)*($A$2:$A2=A2))>1,0,1)
I know there is the FIXED() LoD calculation http://kb.tableau.com/articles/howto/removing-duplicate-data-with-lod-calculations, but I only see use cases of it deduplicating based on another column. However, mine are distinct.
Define a field first_reg_date_per_rep_id as
{ fixed rep_id : min(registration_date) }
The define a field is_first_reg_date? as
registration_date = first_reg_date_per_rep_id
You can use that last Boolean field to distinguish the first record for each rep_id from later ones
try this query
select
rep_id,
user_id,
row_number() over(partition by rep_id order by rep_id,user_id) deduped_rep
from
table

PostgreSQL materialized view with global and partitioned ranks

I have a table with 10 mils. of user ratings.
I need to create materialized view that has global rating and rating by country and is refreshed once a day.
I came up with the following select query:
SELECT row_number() OVER(ORDER BY value DESC, id) AS rank_global,
row_number() OVER(PARTITION BY country ORDER BY value DESC, id) AS rank_country,
*
FROM rate
ORDER BY value DESC, id
LIMIT 100000
Is there a way to speed up this query or maybe there is another way to do the same? I created btree (value desc, id) and (country, value desc, id) indexes but it still takes a lot of time to complete.
Example:
Creating a table and populating it with users with random value column and random country:
CREATE TABLE rate
(
id serial NOT NULL,
name text,
value integer NOT NULL DEFAULT 0,
country character varying,
CONSTRAINT rate_pkey PRIMARY KEY (id)
);
INSERT INTO rate
(SELECT n, ('user_'||n), (random()*30)::int, ('country_'||(random()*3)::int)
FROM generate_series(0,10) AS n);
CREATE INDEX rate_country_value_id_index
ON rate
USING btree(country, value DESC, id);
CREATE INDEX rate_value_id_index
ON rate
USING btree(value DESC, id);
Table contents:
id name value country
0 user_0 28 country_2
1 user_1 24 country_2
2 user_2 29 country_1
3 user_3 11 country_1
4 user_4 16 country_1
5 user_5 28 country_0
6 user_6 3 country_1
7 user_7 7 country_1
8 user_8 28 country_1
9 user_9 4 country_0
10 user_10 29 country_1
Then I create materialized view:
CREATE MATERIALIZED VIEW rate_view AS
SELECT row_number() OVER (ORDER BY value DESC, id) AS rgl,
row_number() OVER (PARTITION BY country ORDER BY value DESC, id) AS rc,
*
FROM rate
ORDER BY value DESC, id;
View contents (rgl - global rank, rc - rank by country):
rgl rc id name value country
1 1 2 user_2 29 country_1
2 2 10 user_10 29 country_1
3 1 5 user_5 28 country_0
4 1 0 user_0 28 country_2
5 3 8 user_8 28 country_1
6 2 1 user_1 24 country_2
7 4 4 user_4 16 country_1
8 5 3 user_3 11 country_1
9 6 7 user_7 7 country_1
10 2 9 user_9 4 country_0
11 7 6 user_6 3 country_1
Now i can create complex queries to select users with closest rank and its neighbours by rank. Both globally and by country.
For example, (after creating (value,id) and (rgl) indexes on view) here is global top 50 and 5 users closest by rank to value 9999942:
(
WITH closest_rank AS
(
SELECT rgl FROM rate_view
WHERE value <= 9999942
ORDER BY value DESC, id ASC
LIMIT 1
)
SELECT rgl, name, value
FROM rate_view
WHERE rgl > (SELECT rgl-3 FROM closest_rank )
ORDER BY rgl ASC
LIMIT 5
)
UNION
SELECT rgl, name, value
FROM rate_view
WHERE rgl <=50
ORDER BY rgl;