Change column type in table - sql

I have a datatable called deal in Oracle with four columns:
DealID: (PK)
LegID
OrigID
Description
The problem is that if I want to insert a deal with description = A, the attributes LegID and OrigID must be unique, otherwise, there is not problem. How can i make this check? I had thought a trigger after insert. There are more solutions?
Thanks in advance!!

You need a function based unique index :
create table tt (
DealID number(10) primary key,
LegID number(10),
OrigID number(10),
Description varchar2(200 char)
);
create unique index tt_leg_orig_dscr_uk on tt (
case when description = 'A' then description end,
case when description = 'A' then legid end,
case when description = 'A' then origid end
);
insert into tt values (1, 1, 1, 'A');
1 row(s) inserted.
insert into tt values (2, 1, 1, 'A');
ORA-00001: unique constraint (XXXXX.TT_LEG_ORIG_DSCR_UK) violated
insert into tt values (2, 1, 2, 'A');
1 row(s) inserted.
select * from tt;
DEALID LEGID ORIGID DESCRIPTION
-----------------------------------
1 1 1 A
2 1 2 A
2 rows returned in 0.01 seconds
insert into tt values (3, 1, 1, 'B');
1 row(s) inserted.
insert into tt values (4, 1, 1, 'B');
1 row(s) inserted.
select * from tt order by 1;
DEALID LEGID ORIGID DESCRIPTION
-----------------------------------
1 1 1 A
2 1 2 A
3 1 1 B
4 1 1 B
4 rows returned in 0.01 seconds
As you can see, the unique index work only with records with description = 'A', It allows to have non-unique records for different descriptions.

Related

Automatically set SUM calculation's result as column value during / after row insert

I have an SQL table called "credits" which contain the following columns:
id (Serial ID for rows (1,2,3,....))
account_id (ID of associated client)
change (int4)
rolling_change
Every time during/after a row insert, I'd like the result of this Query to be the "rolling_change" column's value:
SELECT SUM(change)
FROM credits
WHERE account_id = {account_id} AND id < {this_id};
How can I make this process happen automatically on every row insert?
(I'm using DBeaver for reference)
Here's an example that updates the empty rolling_change after inserts.
CREATE TABLE credits (
id SERIAL PRIMARY KEY,
account_id INT NOT NULL,
change INT NOT NULL,
rolling_change INT
);
CREATE OR REPLACE FUNCTION calc_credits_rolling_change()
RETURNS trigger AS $calc_rolling_change$
BEGIN
UPDATE credits tgt
SET rolling_change = src.rolling_change
FROM (
SELECT id
, SUM(change) OVER (PARTITION BY account_id
ORDER BY id) AS rolling_change
FROM credits
) src
WHERE src.id = tgt.id
AND tgt.rolling_change IS NULL;
RETURN NEW;
END;
$calc_rolling_change$ LANGUAGE plpgsql;
CREATE TRIGGER trg_credits_rolling_change
AFTER INSERT
ON credits
EXECUTE PROCEDURE calc_credits_rolling_change();
INSERT INTO credits (account_id, change) VALUES
(1, 1), (1, 0)
, (2, 1), (2, 1), (2, 0);
INSERT INTO credits (account_id, change) VALUES
(3, 2), (3, 1), (1, 10)
select * from credits order by account_id, id;
id
account_id
change
rolling_change
1
1
1
1
2
1
0
1
8
1
10
11
3
2
1
1
4
2
1
2
5
2
0
2
6
3
2
2
7
3
1
3
Demo on db<>fiddle here

Find row with the most true value columns

Table User
estado_id
user_id
shopping
mercado
feira
1
1
1
0
1
1
2
1
1
1
1
3
1
0
1
Table Cidade
estado_id
version_id
name
type
1
1
jose
shopping
1
2
maria
feira
1
2
maria
mercado
1
3
bruna
mercado
I need to compare the states, and bring only those that cover user x criteria's, but the user table may contain hundreds of users and I just have to return to which user x is bound. In the users table, I need to bring everyone that they think matches. If in the shopping and fair table the value is 1 and market to 0 then shopping and fair must be returned within these versions, I managed to bring up only the user data / id_state, however when I compare the user table = 1 with the city table type = shopping
But I cannot find a way to do that, because' it needs to be dynamic
WITH CTE AS (
SELECT cidade.*, uv.user_id, uv.shopping, uv.mercado, uv.feira
FROM cidade
INNER JOIN userst as uv ON cidade.estado_id = uv.estado_id
WHERE cidade.estado_id = 1 AND uv.user_id = 1
)
SELECT [type], shopping, mercado, feira
FROM CTE
My expectation
estado_id
version_id
name
type
user_id
1
1
jose
shopping
1
CREATE TABLE cidade(
estado_id int,
version_id int,
name varchar(255),
[type] varchar(255)
);
CREATE TABLE userst (
estado_id int,
user_id int,
shopping int,
mercado int,
feira int
);
INSERT INTO cidade (estado_id, version_id, name, [type])
VALUES (1, 1, 'jose', 'shopping')
, (1, 2, 'maria', 'feira')
, (1, 2, 'maria', 'mercado')
, (1, 3, 'bruna', 'mercado');
INSERT INTO userst (estado_id, user_id, shopping, mercado, feira)
VALUES (1, 1, 1, 0, 1)
, (1, 2, 1, 1, 1)
, (1, 3, 1, 0, 1);

How to insert values in table if there is a primary key on id field in it?

I have trouble, I have table named my_table)
id name
1 A
2 B
3 C
and I want to insert top 2 so, values have autoincrement automaticaly like this
insert into my_table (name) values (A), (B);
Is this possible in postgressql?
May be I should have count() + 1, then count() + 2 here
insert into my_table (id, name) values (count(*) + 1, A), (count(*) + 2, B);
or something like this
because my id has constraint
BIGSEREIAL PRIMARY KEY NOT NULL
And I cant add values without getting the last id in table.
You can do:
create table my_table (
id int not null generated always as identity,
name varchar(10) not null
);
insert into my_table (name) values ('A'), ('B');
Then:
select * from my_table;
Result:
id name
-- ----
1 A
2 B
See running example at db<>fiddle.

SQL: Update and Insert with condition

I would like to know if this is possible. I would like to update a record only if the typeId equal my value and add a record in table B if that's the case.
TableA:
id (PK, int)
typeId (int)
TableB:
id (PK, int)
tableAId (FK, int)
note (nvarchar)
My SQL script:
UPDATE [dbo].[TableA]
SET [TypeId] = CASE
WHEN [TypeId] = 4 THEN 6 AND
(INSERT INTO [dbo].[TableB] ([tableAId],[note])
VALUES ([dbo].[TableA].Id,'type has changed'))
ELSE [Id]
END
The script above looks like I want to achieve but obviously it's incorrect. How can I do multiple things in my case condition? Update a value and insert a record with the current id?
Data sample:
Table A (id, typeId)
1, 4
2, 5
3, 2
Table B (id, tableAid, note)
1, 1, 'note1'
2, 1, 'note2'
3, 2, 'note1'
Should become:
Table A (id, typeId)
1, 6
2, 5
3, 2
Table B (id, tableAid, note)
1, 1, 'note1'
2, 1, 'note2'
3, 2, 'note1'
4, 1, 'type has changed'
Try to use OUTPUT clause.
Test tables and data:
CREATE TABLE A(
id int NOT NULL PRIMARY KEY,
typeId int NOT NULL
)
INSERT A(id,typeId)VALUES(1, 4),(2, 5),(3, 2)
CREATE TABLE B(
id int NOT NULL IDENTITY PRIMARY KEY,
tableAid int NOT NULL REFERENCES A(id),
note varchar(50) NOT NULL
)
SET IDENTITY_INSERT B ON
INSERT B(id,tableAid,note)VALUES(1, 1, 'note1'),(2, 1, 'note2'),(3, 2, 'note1')
SET IDENTITY_INSERT B OFF
Using OUTPUT demo:
DECLARE #LogTable TABLE(tableAid int,note varchar(50))
UPDATE A
SET
typeId=6
OUTPUT inserted.id,'type has changed'
INTO #LogTable(tableAid,note)
WHERE typeID=4
INSERT B(tableAid,note)
SELECT tableAid,note
FROM #LogTable
If you drop the foreign key in the table B then you can use OUTPUT directly into table B without #LogTable:
-- table B without FOREIGN KEY
CREATE TABLE B(
id int NOT NULL IDENTITY PRIMARY KEY,
tableAid int NOT NULL, --REFERENCES A(id),
note varchar(50) NOT NULL
)
-- only one UPDATE with OUTPUT
UPDATE A
SET
typeId=6
OUTPUT inserted.id,'type has changed'
INTO B(tableAid,note)
WHERE typeID=4

SQL Server - break down a payment correctly

This is the problem I am having
Example:
Total Value: £1550.00
Monthly Payments 12
This calculates at 129.16666666666666667, however you wouldn't show a money value like this, it would display as £129.17, which equals £1550.04, which is wrong
Question
Is it possible to remove the float/decimal value from 11 of the 12 installments , and only display it in the first or final payment?
Example Result
PaymentNumber Value
1 £129.00
2 £129.00
3 £129.00
4 £129.00
5 £129.00
6 £129.00
7 £129.00
8 £129.00
9 £129.00
10 £129.00
11 £129.00
12 £131.00
thanks for any help, and any suggestions of another way I could do this would be very grateful..
I have included the code for a table i am using to test this... and also added in the data below. for now using an update statement will be acceptable and i will try to make the code more efficient at a later date. I do have a procedure running that inserts the monthly breakdowns programmatically.
Table info I am using
CREATE TABLE CustomerFinance (CustomerFinanceID int identity (1,1) not null,
TotalValueOwed decimal(12,4), --1550.00
LengthOfContract int) --LENGTGH IN MONTHS, i.e. 12
CREATE TABLE CustomerFinanceLine (CustomerFinanceLineID int identity (1,1) not null,
CustomerFinanceID int, --FOREIGN KEY LINK
PaymentNumber int, --1, 2, 3 AND SO ON
PaymentValue decimal(12,4)) --THE MONTHLY BREAKDOWN COSTS
--KEYS
alter table CustomerFinance add constraint CustomerFinanceID_PK PRIMARY KEY (CustomerFinanceID)
alter table CustomerFinanceLine add constraint CustomerFinanceLineID_PK PRIMARY KEY (CustomerFinanceLineID)
alter table CustomerFinanceLine add constraint CustomerFinanceID_FK FOREIGN KEY (CustomerFinanceID) REFERENCES CustomerFinance(CustomerFinanceID)
--PaymentNumber COUNTER (RUNS IN A PROCEDURE)
CREATE PROCEDURE FinanceCounter AS
;WITH MyCTE AS
(
SELECT *,
ROW_NUMBER() OVER(PARTITION BY CustomerFinanceID ORDER BY CustomerFinanceLineID) AS NewVariation
FROM CustomerFinanceLine
)
UPDATE MyCTE
SET PaymentNumber = NewVariation
WHERE PaymentNumber IS NULL
Data
--inserted PaymentValue as null for now, ideally i will
-- have a procedure to do this and insert the breakdowns programmatically
--for now an update statement will do fine unless its easier to insert it
INSERT INTO CustomerFinance VALUES (1550.00, 12)
INSERT INTO CustomerFinanceLine VALUES (1, 1, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 2, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 3, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 4, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 5, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 6, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 7, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 8, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 9, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 10, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 11, NULL)
INSERT INTO CustomerFinanceLine VALUES (1, 12, NULL)
You just need to do something like
WITH T(PaymentNumber) AS
(
SELECT 1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9 UNION ALL
SELECT 10 UNION ALL
SELECT 11 UNION ALL
SELECT 12
)
SELECT CASE WHEN PaymentNumber < 12 THEN FLOOR(1550.00/12)
WHEN PaymentNumber = 12 THEN 1550.00 - 11*FLOOR(1550.00/12)
END
FROM T
Let me assume you have a list of numbers. Second, in my experience, earlier payments would be rounded up, with the final being less than that payment (your title is doing this "correctly"):
select n.n as PaymentNumber,
(case when n.n < #NumPayments then ceiling(#Amount / #NumPayments)
else #Amount - #NumPayments * ceiling(#Amount / #NumPayments)
end) as MonthlyAmount
from numbers n
where n.n <= #NumPayments;
Of course, you can do the same thing with FLOOR(). Note: this might need some adjustment for small amounts.
EDIT:
n.n is just the numbers. You can use a CTE. Here is an example:
with numbers as (
select 1 as n
union all
select n + 1
from numbers
where n < 20
)