Postgres table constraint using group-by - postgresql

I have a Postgres table that looks like this:
Category Unit Default Unit
---------------------------------------
Currency USD True
Currency EURO False
Currency AUS False
Length Kilometer True
Length Mile False
Length Foot False
Length Inch False
Mass Kilogram True
I want to set a table constraint so that there can be only one 'Default Unit' per Category.
Can this be done using Group By in a constraint, perhaps?

You can create a unique partial index:
create unique index idx_table(category, default_unit) on table(category, default_unit)
where default_unit;

Related

Transpose a part of a table saving column names as a new column value [PostgreSQL]

how can I transpose a part of a table saving column names as a new column value and merge these columns values into one?
Are there any ways to do this without hardcoding?
For example, I have table with such structure:
I've tried to use crosstab but didn't get how to extract column names. Also have tried to iterate over columns names generating a set of records but main problem is that source table is enormous.
StoreHouse
Product
HasDiscount
IsOutOfStock
JohnStore
chair
False
True
SomeStore
table
True
False
As a result I need output of such structure:
StoreHouse
Product
Parameter
Status
JohnStore
chair
HasDiscount
False
JohnStore
chair
IsOutOfStock
True
SomeStore
table
HasDiscount
True
SomeStore
table
IsOutOfStock
False
Barring dynamic SQL, you need to know the column names and "hardcode" them in your table. Apart from that, you just need a union:
SELECT
"StoreHouse",
"Product",
'HasDiscount' AS "Parameter",
"HasDiscount" AS "Status"
FROM example
UNION ALL
SELECT
"StoreHouse",
"Product",
'IsOutOfStock' AS "Parameter",
"IsOutOfStock" AS "Status"
FROM example
Optionally order by StoreHouse and Product.

activerecord unique constraint on multiple records

how should I write the activerecord migration to reflect this :
CREATE TABLE table (
c1 data_type,
c2 data_type,
c3 data_type,
UNIQUE (c2, c3)
);
This adds a unique constraint on one column, but what I'm looking for is to create the unique constraint on the combination of 2 columns, like explained in the section Creating a UNIQUE constraint on multiple columns.
EDIT
More precisely: I have a table account and a table balance_previous_month.
class CreateBalance < ActiveRecord::Migration[6.1]
def change
create_table :balance_previous_month do |t|
t.decimal :amount, :precision => 8, :scale => 2
t.date :value_date
t.belongs_to :account, foreign_key: true
t.timestamps
end
end
end
Since we're in January, the value date (i.e. balance at the end of the previous month) is 2020-12-31.
I want to put a constraint on the table balance_previous_month where per account_id, there can be only one value_date with a given amount. The amount can be updated, but a given account can't have 2 identical value_dates.
The link you added to the other post is not exactly equivalent to your request since one answer talks about enforcing uniqueness through the model while the other talks about using an index while in your example you are using a constraint. (Check this for more information on the difference between them).
There are 2 places where you can enforce uniqueness, application and database and it can be done in both places at the same time as well.
Database
So if you want to enforce uniqueness by using an index you can use this:
def change
add_index :table, [:c2, :c3], unique: true
end
If you want to add a constraint as in your example you will have to run a direct sql query in your migration as there is no built-in way in rails to do that.
def up
execute <<-SQL
ALTER TABLE table
ADD UNIQUE (c2, c3)
SQL
end
Check the link above for more info about the difference between them.
Application
Enforcing uniqueness through the model:
validates :c2, uniqueness: { scope: :c3 }
Thanks to Daniel Sindrestean, this code works :
class CreateBalance < ActiveRecord::Migration[6.1]
def change
create_table :balance_previous_month do |t|
t.decimal :amount, :precision => 8, :scale => 2
t.date :value_date
t.belongs_to :account, foreign_key: true
t.timestamps
end
execute <<-SQL
ALTER TABLE balance_previous_month
ADD UNIQUE (account_id, value_date) # ActiveRecord creates account_id
SQL
end
end

How to create an Index on a boolean column

My DB table has one primary key and a number of integer columns and one boolean column called paused which is not in the primary key. This table will only ever hold a few hundred rows but I need to query the boolean column very regularly. I need to know if any row in the boolean paused column is true, if one row is true I will return true if all are false I will return false.
Should I create an index on the boolean column and what would that syntax look like or is there any other way to optimize that query?
CREATE TABLE IF NOT EXISTS pause_metrics (
consumer TEXT NOT NULL,
timstamp TIMESTAMP NOT NULL,
idle_counter INTEGER NOT NULL,
paused BOOLEAN DEFAULT FALSE NOT NULL,
PRIMARY KEY(consumer)
);
To support the following query:
SELECT paused
from pause_metrics
where paused
limit 1;
A filtered index would be the most efficient thing:
create index idx_paused on pause_metrics(paused)
where paused;
The actual column in the index doesn't really matter, the important part is the where paused which only indexes the rows that have paused = true.
To find out if all rows have paused = false, you can use an exists query:
select not exists (SELECT 1 from pause_metrics where paused limit 1) as all_active
This will make use of the filtered query and should be quite quick.

Unique constraint on single field of a custom type in postgres

I have an entity price in my schema it has an attribute amount which is of a custom type money_with_currency.
The money_with_currency is basically type (amount Big Int, currency char(3)).
The price entity belongs to a product. What I want to do is, create a unique constraint on the combination of product_id(foreign key) + currency . How can I do this?
Referencing a single field of a record type is a bit tricky:
CREATE TYPE money_with_currency AS (amount bigint, currency char(3));
CREATE TABLE product_price
(
product_id integer not null references product,
price money_with_currency not null
);
CREATE UNIQUE INDEX ON product_price(product_id, ((price).currency));

Postgres sort expression

I have a table with goods:
CREATE TABLE public.goods (
"id" bigserial NOT NULL,
title varchar(250) NOT NULL,
cost numeric(10,2),
PRIMARY KEY ("id")
);
Now I want to sort this table by title but put all goods with cost 0 at the end of the list. Is this possible?
If I try to use:
ORDER BY
cost DESC,
title ASC
I get incorrect order by title
One way to do this is to use a CASE expression when ordering which places the block of records having a zero cost at the bottom. Then, within each block (either zero cost or non-zero cost), the records can be sorted alphabetically by the title.
SELECT cost, title
FROM public.goods
ORDER BY CASE WHEN cost = 0 THEN 1 ELSE 0 END,
title