KanBan Database architecture - ruby-on-rails

I'm trying to implement a kanban agile board like Trello in my application. I'm wondering how to do it the best way. I'm considering these entities:
Board
has many Lists
List
has many Cards
Card
contains some content
However I'm stuck with ordering of Cards. Each Card should have a sort position to sort Cards in particular order in a List. When dragging a Card, I should change it's position and save it in the database. What is the most efficient way of doing this?
Adding a position field to each Card seems redundant since I would have to recalculate positions of all Cards in a List (or two) after I drag a Card to a different position (there will be hundreds of Cards). I was thinking of storing an array of all Card ids in a List and sort Cards by this array. What are pros/cons of this solution? And are there any better solutions?
I'm using Ruby on Rails and PostgreSQL.
UPDATE
Using #cske answer I came up with the following solution:
CREATE OR REPLACE FUNCTION move_buyer_card(
new_list_id INT
, param_id INT
, new_position INT
) RETURNS FLOAT4
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
var_lower_bound FLOAT4;
var_upper_bound FLOAT4;
var_new_weight FLOAT4; /*between 0 and 1*/
BEGIN
IF new_position < 2 THEN /*first position*/
var_lower_bound := 0;
SELECT MIN(weight) FROM Buyers
WHERE board_list_id = new_list_id
INTO var_upper_bound;
IF var_upper_bound IS NULL THEN /*empty list*/
var_upper_bound := 1;
END IF;
ELSE /*not first position*/
WITH ordered_cards AS (
SELECT id, RANK() OVER (ORDER BY weight ASC) AS rank, weight
FROM Buyers WHERE board_list_id = new_list_id
)
SELECT cards0.weight, cards1.weight from ordered_cards cards0
JOIN ordered_cards cards1
ON cards0.rank = cards1.rank - 1
WHERE cards1.rank = new_position
INTO var_lower_bound, var_upper_bound;
IF NOT FOUND THEN /*only 1 item in list OR last position*/
SELECT MAX(weight) FROM Buyers WHERE board_list_id = new_list_id
INTO var_lower_bound;
IF var_lower_bound IS NULL THEN /*empty list*/
var_lower_bound := 0;
END IF;
var_upper_bound := 1;
END IF;
END IF;
var_new_weight := var_lower_bound + (var_upper_bound - var_lower_bound) / 2;
UPDATE Buyers
SET weight = var_new_weight,
board_list_id = new_list_id
WHERE id = param_id;
RETURN var_new_weight;
END;
$$;

Consider this, trick is not storing the position, but a weight so you can insert between any two elements
create table listOfCards (
listId INTEGER
,cardId INTEGER
,weigth FLOAT4
,PRIMARY KEY (listId,cardId)
);
CREATE OR REPLACE FUNCTION addCard(
plistId INT
, pcardId INT
, ppos INT
) RETURNS FLOAT4
LANGUAGE plpgsql SECURITY DEFINER
AS $$
DECLARE
vlb FLOAT4;
vub FLOAT4;
vnw FLOAT4;
BEGIN
IF 2 > ppos THEN
vlb := 0;
SELECT min(weigth) FROM listOfCards WHERE listId = plistId INTO vub;
IF vub IS NULL THEN /*empty list*/
vub := 2;
END IF;
ELSE
with corder as (select cardId,RANK() OVER (order by weigth asc) as r,weigth FROM listOfCards WHERE listId=1)
select c0.weigth,c1.weigth from corder c0 JOIN corder c1 ON c1.r = c0.r + 1 where c1.r = ppos INTO vlb,vub;
IF NOT FOUND THEN
SELECT max(weigth) FROM listOfCards WHERE listId = plistId INTO vlb;
IF vlb IS NULL THEN /*empty list*/
vlb := 0;
END IF;
vub := (vlb+1) * 2;
END IF;
END IF;
vnw := vlb + (vub-vlb) /2;
INSERT INTO listOfCards(listId, cardId, weigth)
VALUES (plistId,pcardId,vnw )
ON CONFLICT ON CONSTRAINT listofcards_pkey DO UPDATE SET weigth = vnw ;
RETURN vnw;
END;
$$;
Usage:
select addCard(1,1,1);
select addCard(1,2,1);
select addCard(1,3,2);
select addCard(1,4,2);
select addCard(1,5,5);
select addCard(1,5,2);
select * from listOfCards ORDER BY weigth;
Result:
1,2,0.5
1,5,0.5625
1,4,0.625
1,3,0.75
1,1,1

Related

Count Field Crystal Report

I have some view from database
here the view
select main.processId,
main.departmentname as DepartmentName,
dpaudit.DropdownlistName DdlAudit,
main.PICForAudit,
main.DateOfAudit,
main.ReferenceDetail,
main.finding as Finding,
CASE dpfinding.DropdownlistName WHEN 'RFI' THEN 'X' ELSE '' END AS IsRFi,
CASE dpfinding.DropdownlistName WHEN 'Minor NC' THEN 'X' ELSE '' END AS IsMinor,
CASE dpfinding.DropdownlistName WHEN 'Major NC' THEN 'X' ELSE '' END AS IsMajor,
iird.description as Description,
CASE spdl.DropdownlistName WHEN 'High Priority' THEN 'X' ELSE '' END AS High,
CASE spdl.DropdownlistName WHEN 'Medium Priority' THEN 'X' ELSE '' END AS Medium,
CASE spdl.DropdownlistName WHEN 'Low Priority' THEN 'X' ELSE '' END AS Low,
bc.DropdownlistName as RootCause,
iird.CorrectiveActionPlan,
iird.picResponsibilityName,
iird.TargetDateCompletion,
pos.positionname as Position
from SheAuditMain main
join IncidentInvestigationRecommendationDescription iird on main.ProcessId = iird.PIDSource
join SheProgramsDropdownlistMenu spdl on spdl.dropdownlistcode = iird.priority
join SheProgramsDropdownlistMenu dpaudit on main.TypeOfAudit = dpaudit.DropdownlistCode
join SheProgramsDropdownlistMenu dpfinding on main.TypeOfFinding = dpfinding.DropdownlistCode
join SheProgramsDropdownlistMenu bc on bc.dropdownlistcode = iird.basiccause
join vwusrposOs pos on iird.picResponsibility = pos.positionid
and then i want to create report using crystal report so the report look like this
https://ibb.co/eq924T
as we can see count of the priority [L,M,H]
isn't correct,
i do some code for that
first, i add running total field from each field [L,M,H]
and then i write code in formula
example for formula field for field high, because low and med is the same
Local numberVar i := 0;
Local numberVar TotalHigh := 0;
for i := 0 to {#RTotalHigh} - 1 do <{#RTotalHigh} coming from running total
(
if ({VwCheckPrior.High} = 'X') then
TotalHigh := TotalHigh + 1
else
0
);
TotalHigh
how do i display the correct calculation ?
[sorry for the image, i forgot my account before]
I think you should use NULL instead off empty string ('') in that CASE, because when you use empty string it will be count in total

How to get result of complex query in redbeanphp

So I have this query
$sql = "set #row_number := 0;
SELECT *
FROM (
SELECT user_id, #row_number := #row_number +1 pos
FROM user
ORDER BY plays DESC
) AS row_to_return
WHERE user_id = '{$_SESSION['user']->user_id}'";
echo $sql;
$user_pos = R::exec($sql);
Have tried to use exec, findAll, findRow - no result.
This thing did the trick:
$sql = "SELECT *
FROM (
SELECT user_id, #row_number := #row_number +1 pos
FROM user
ORDER BY plays DESC
) AS row_to_return
WHERE user_id = '{$_SESSION['user']->user_id}'";
$set_sql = 'set #row_number := 0';
R::exec($set_sql);
$user_pos = R::getRow($sql);

Use MERGE statement in a stored procedure with Informix

I have a stored procedure like this : http://tinyurl.com/pbk8qfb
CREATE PROCEDURE PS_TAB_TSEN(NOMFIC VARCHAR(70), LIBSEN VARCHAR(35),
CODAPPFOF VARCHAR(3))
DEFINE NUMINTNAV VARCHAR(10);
BEGIN;
LET NUMINTNAV = '0';
SELECT TO_CHAR(NUM_INT_NAV) INTO NUMINTNAV
FROM TAB_INAV WHERE NOM_FIC = NOMFIC;
MERGE INTO TAB_TSEN T
USING (SELECT COD_SEN, LIBSEN AS LIB_SEN, CODAPPFOF AS COD_APP_FOF
FROM TAB_TSEN
WHERE COD_SEN = NUMINTNAV) AS S
ON (T.COD_SEN = S.COD_SEN)
WHEN MATCHED THEN
UPDATE SET T.COD_SEN = S.COD_SEN, T.LIB_SEN = S.LIB_SEN,
T.COD_APP_FOF = S.COD_APP_FOF
WHEN NOT MATCHED THEN
INSERT (T.COD_SEN, T.LIB_SEN, T.COD_APP_FOF)
VALUES (S.COD_SEN, S.LIB_SEN, S.COD_APP_FOF);
COMMIT;
END PROCEDURE;
When I run it all is good, but when I check in the table I see no result, for the insert statement and for the update statement too.
I call it like this:
CALL PS_TAB_TSEN('~/PSS/Order/OrderList.aspx', 'Liste des commandes', 'NET');
I don't understand what is going on...
Problem solved, I replace my stored procedure by this one :
CREATE PROCEDURE PS_TAB_TSEN(NOMFIC VARCHAR(70), LIBSEN VARCHAR(35), CODAPPFOF VARCHAR(3))
DEFINE ISPRESENT INTEGER;
DEFINE NUMINTNAV VARCHAR(10);
BEGIN;
LET NUMINTNAV = '0';
SELECT TO_CHAR(NUM_INT_NAV) INTO NUMINTNAV FROM TAB_INAV WHERE NOM_FIC = NOMFIC;
LET ISPRESENT = 0;
SELECT COUNT(*) INTO ISPRESENT FROM TAB_TSEN WHERE COD_SEN = NUMINTNAV;
IF(ISPRESENT > 0) THEN
UPDATE TAB_TSEN SET T.COD_SEN = S.COD_SEN, T.LIB_SEN = S.LIB_SEN, T.COD_APP_FOF = S.COD_APP_FOF
ELSE
INSERT INTO TAB_TSEN (T.COD_SEN, T.LIB_SEN, T.COD_APP_FOF) VALUES (S.COD_SEN, S.LIB_SEN, S.COD_APP_FOF);
END IF;
COMMIT;
END PROCEDURE;

Checking if a value is a decimal in an input

I'm learning SQL using SQLAnywhere which I believe uses a fairly standard SQL syntax
My problem is I have created a table MatchRecord with an Id as char(4) NOT NULL, a score as decimal and a pins as decimal.
now I want to create a procedure insert_scores to insert values into the table
I have got so far as :
create procedure insert_scores(IN play_id char(4), IN play_score decimal(5, 2),
IN no_pins decimal(5, 2), OUT a_message varchar(40))
begin
if substr(play_id, 1, 1)in (Upper('M','F', 'J'
then
if isnumeric(substr(play_id 2, 3)) = 1
then
if isnumeric(play_score) = 1
then
if isnumeric(no_pins) = 1
then
insert into MatchRecord(id, score, pins)
values(play_id, play_score, no_pins);
set a_message = 'Entry successful';
else
set a_message = 'Number of pins must be decimal ie, 1.6 ';
end if;
else
set a_message = 'Score must be decimal ie, 9.4 ';
end if;
else
set a_message = 'ID number must be in range 000 to 999 ';
end if;
else
set a_message = 'First character of ID must be M, F of J':
end if;
end
this works fine apart for any accidental insertion of a character in either of the decimal values, whereupon the system throws an error, it seems to check the table type before it reads the if statement,
I have tried isnumeric(string(play_score)) = 1 but still the same error.
Is there any way of checking that the number passed in play_score and no_pins is a decimal before the first if statement?
You could try to transform your number into a string and then check if there is a dot in the string. Something like this could do the trick.
DECLARE #number_is_ok BIT
SET #number_is_ok = CASE charindex('.', CAST(play_score as CHAR))
WHEN 0 THEN 0
ELSE 1
END
You could do then a simple check if the number is decimal or not and then continue with the corresponding logic.
IF #number_is_ok = 1 ...

How to return ref_cursor from for loop from pl/sql procedure

i want to get back ref cursor for the below sp. But it's not working.. not getting any records.. So please help me for this.
DATA_INPUTLIST - it's collection type for two variables (mtn, mtnEffDate)
DATA_RESULTLIST - It's also a collection type for 3 variables (id, effDate, mtn)
CREATE OR REPLACE PROCEDURE proc1 (
pCustId IN NUMBER,
pAcctNo IN NUMBER,
pSearchCriteria IN DATA_INPUTLIST,
pRecordSet OUT SYS_REFCURSOR,
out_error_code OUT NUMBER,
out_error_message OUT VARCHAR2
) AS
--Variables
v_SUN_DATE DATE := TO_DATE('01/15/2011', 'mm/dd/yyyy');
vCount NUMBER := 0;
v_mtn NUM
BER;
v_mtn_eff_date DATE;
cMtnPricePlanInfo DATA_RESULTLIST;
BEGIN
SELECT COUNT(*) INTO vCount FROM TABLE (pSearchCriteria);
FOR i IN 1..vCount LOOP
SELECT MTN, TO_DATE(MTN_EFF_DATE, 'mm/dd/yyyy') into v_mtn, v_mtn_eff_date
FROM TABLE (pSearchCriteria)
WHERE
ROWNUM = i;
SELECT
A.PPLAN_ID, A.EFF_DATE, A.MTN INTO cMtnPricePlanInfo(i).PPLAN_ID, cMtnPricePlanInfo(i).EFF_DATE, cMtnPricePlanInfo(i).MTN
FROM CUST_ACCT_LINE_PPLAN A, CUST_ACCT_LINE_PPLAN_HIST B
WHERE
A.CUST_ID = pCustId
AND A.ACCT_NO = pAcctNo
AND A.MTN = v_mtn
AND A.MTN_EFF_DATE = v_mtn_eff_date
AND A.EFF_DATE >=
(SELECT MAX(EFF_DATE) FROM CUST_ACCT_LINE_PPLAN_HIST C
WHERE
C.CUST_ID = pCustId
AND C.ACCT_NO = pAcctNo
AND C.MTN = v_mtn
AND C.MTN_EFF_DATE = v_mtn_eff_date
AND C.EFF_DATE <= v_SUN_DATE)
AND A.CUST_ID = B.CUST_ID
AND A.ACCT_NO = B.ACCT_NO
AND A.MTN = B.MTN
AND A.MTN_EFF_DATE = B.MTN_EFF_DATE
AND A.PPLAN_ID = B.PPLAN_ID;
END LOOP;
OPEN pRecordSet FOR
SELECT * FROM TABLE (cMtnPricePlanInfo);
END;
/
The first things that stands out to me is the following:
SELECT MTN, TO_DATE(MTN_EFF_DATE, 'mm/dd/yyyy') into v_mtn, v_mtn_eff_date
FROM TABLE (pSearchCriteria)
WHERE
ROWNUM = i;
For i greater than 1 this will not return anything. For the reason why, see this question. In fact, because you're using SELECT ... INTO ..., you'll get a no data found error if i is greater than or equal to 2.
You don't need an SQL query just to fetch some values out of PL/SQL collections. Try replacing this query with
v_mtn := pSearchCriteria(i).MTN;
v_mtn_eff_date := TO_DATE(pSearchCriteria(i).MTN_EFF_DATE, 'mm/dd/yyyy');
I don't have your tables nor the data in them so I can't be sure why you're getting no data. I can only hazard at some obvious suggestions: is vCount zero? If you put suitable values for v_mtn and v_mtn_eff_date and run your query on its own, separate from this stored procedure, does it return any rows?

Resources