To give you context here is my db schema.
Fruit Table
Id Name
1 Gala Apples
2 Navel Oranges
3 Peach
4 Mandarin Oranges
5 Kiwi
6 Fuji Apples
Intersect Table:
FruitId CrossRefFruitId
1 6
2 4
So my intersect table has the fruit values.
1 (Gala Apples) are related to 6 (Fuji Apples)
2 (Navel Oranges) are related to 4 (Mandarin Oranges)
In the UI say the user is viewing details about Gala Apples. I want to be able to have a "See Also: Fuji Apples".
Now I'm told that I do NOT want to have reverse entries so 1,6 is good but it is a waste to have 6,1 which makes sense.
So I'm trying to write a TSQL check that will identify if 1,6 or 6,1 exists.
Ultimately this would go into an INSERT trigger that would check the incoming row to see if the combination exists then allow insert or skip.
Googling I find REVERSE which is for reversing a string and end up with a lot of hits for EXCEPT and INTERSECT.
So even just knowing the proper terminology to google so I can self educate would be appreciated.
create table #test
(
id1 int,
id2 int
)
insert into #test
select 1,6
union all
select 2,5
union all
select 6,1
union all
select 5,3
union all
select 5,2
select * from #test t1
where exists(
select 1 from #test t2 where t1.id1=t2.id2 and t1.id2=t2.id1)
Updating as per question:
Your trigger should contain below line of code which checks existence of rows which are reversed..
if exists(
select 1 from
#test t1
join
inserted i
on i.id1=t1.id2 and i.id2=t1.id1
)
begin
rollback tran
--some message---
end
Related
I have paste the details as below,
The detailed requirements are here:
Sno Column A Column B Column C Formula
1 0 -1 Yrs 0.963190184 100000
2 1-4 Yrs 0.992394232 96319 (B1 * C1 (default value 100000)) =C2
3 5 - 9 Yrs 0.994964922 95586 (B2 * C2 ) =C3
4 10 - 14 Yrs 0.998372661 95105 (B3 * C3 ) =C4
5 15 - 19 Yrs 0.994485603 94950
6 20- 24 Yrs 0.992903887 94427
7 25 - 29 Yrs 0.994008987 93757
8 30 - 34 Yrs 0.994041445 93195
9 35 - 39 Yrs 0.991283828 92640
10 40 - 44 Yrs 0.987141228 91832
The above same formula needs to be apply for the remaining columns through a PostgreSQL query.
Since your basic requirement is looking back to the prior row the lag function first comes to mind. However this dos not work as the necessary value is being calculated of the fly and lag operates on the result set (prior to lag operation). So we turn to a recursive query where at each recursion we have available the just calculated on the prior recursion. This of course assumes the other columns are already loaded. We get the desired values with:
with recursive demo( rno, r_colb, r_colc) as
( select 0, 1::numeric, 100000::numeric -- prime the recursion
union all
select sno, column_b, r_colb*r_colc
from test
join demo
on (sno = rno+1) -- next sno
)
select rno, r_colb, round(r_colc) r_colc
from demo ;
That does not get to the final need - updating the table. Fortunately, Postgres supports "update ... from (subquery) where ..." structure and the subquery just basically needs to be a valid query; including the above. So we arrive at:
update test
set column_c = r_colc
from (
with recursive demo( rno, r_colb, r_colc) as
( select 0, 1::numeric, 100000::numeric -- prime the recursion
union all
select sno, column_b, r_colb*r_colc
from test
join demo
on (sno = (select min(sno) -- since I do trust autogenerated ids
from test -- to actually be in perfect sequence
where sno > rno))
)
select rno, r_colb, round(r_colc) r_colc
from demo
) s
where sno = rno;
See demo run in v9.6.
In response to "applied for continuous calculation", yes that is possible and actually only changing the WITH clause to prime the recursion. But first, when speaking on SQL remove IF from your vocabulary - there is no such thing. There are conditionals (where, case, when...) but no IF.
The non-recursive query just gets the last known value then the recursive part looks at each subsequent and calculates column_c from it. To restart the calculation, just do the same. I modified the update query to do just that. See revised demo. It would have been possible to just modify the non-recursive query to select sno 10 (the last known value for column_c). But doing so would require updating the query for subsequent execution. Instead it looks for the last know (not null column_c) selecting that as the primer. This does require all new rows leave column_c null. But it should be good for any number of additional rows.
Note: The term "prime/primer" is my terminology for this query. It is not a general technical term used with a recursive CTE.
update test
set column_c = r_colc
from (
with recursive demo( rno, r_colb, r_colc) as
( select sno, column_b, column_c -- prime the recursion
from test
where sno = (select max(sno)
from test
where column_c is not null
)
union all
select sno, column_b, r_colb*r_colc
from test
join demo
on (sno = (select min(sno) -- since I do trust autogenerated ids
from test -- to actually be in perfect sequence
where sno > rno))
)
select rno, r_colb, round(r_colc) r_colc
from demo
) s
where sno = rno;
I have a result set in a temp table that is the result of some complicated joins and need to know the best way to filter rows that have the same duplicate AccountId/HealthPlanId (shown below).
select * from #HealthPlans
And the contents are as follows:
AccountId MemberId HealthPlanId RankNo
101273 47570 5215 1
101273 47570 2187 2
101273 55551 5179 3
160026 48102 5620 1
160026 48446 5620 2
In this scenario RankNo, which is not a value computed by my original query, is a db column that ranks member/healthPlan where there is more than one member/healthPlan combination on a given account.
In the case of account 101273, I have the same member (47570) with 3 separate health plans (5215, 2187, 5179). That's fine. I want to rank the health plans.
However, for accountId 160026, I have healthPlanId: 5620 listed twice but with different memberId's. I need to keep either of these member id's and discard the other (it doesn't matter which I keep since I'm only interested in Ranking the HealthPlanId).
Basically, an account should only have a row for each unique health plan. However, duplicate memberId's is OK and should be ranked as long as the HealthPlanId differs.
In other words, select rows from #HealthPlans such that the following is the result set:
AccountId MemberId HealthPlanId RankNo
101273 47570 5215 1
101273 47570 2187 2
101273 55551 5179 3
160026 48102 5620 1
There's no need to show the original joins because this is basically a simplification of my original issue.
Thanks,
Sean
Another method using a window function:
DECLARE #tab TABLE (AccountId int, MemberId int, HealthPlanId int, RankNo int)
INSERT #tab VALUES
(101273,47570,5215,1),
(101273,47570,2187,2),
(101273,55551,5179,3),
(160026,48102,5620,1),
(160026,48446,5620,2)
SELECT *
FROM(
SELECT ROW_NUMBER() OVER(PARTITION BY t.AccountId, t.HealthPlanId ORDER BY t.RankNo) rn, t.*
FROM #tab t
) t2
WHERE t2.rn = 1
Your particular query might look like:
SELECT *
FROM(
SELECT ROW_NUMBER() OVER(PARTITION BY hp.AccountId, hp.HealthPlanId ORDER BY hp.RankNo) rn, hp.*
FROM #HealthPlans hp
) hp2
WHERE hp2.rn = 1
I want my Firebird SQL to loop through part of the code WHILE a condition is meet.
Initially I didn't even think it was possible. However I have done some reading and now believe that I can use WHILE loop.
I understand a FOR loop is not what I want as it applies to the whole code, not just part of it.
I am using this in Excel and could use some VBA code to do what I want, but it would be better if I can do it all via Firebird SQL as then I can apply it elsewhere.
SELECT
'1' as "Qty",
'of ' || ALP3.PROPERTYVALUE AS "Total Qty"
FROM ASSEMBLYLINES
LEFT JOIN ASSEMBLYLINEPROPS ALP1 ON ALP1.HEADERSYSUNIQUEID = ASSEMBLYLINES.SYSUNIQUEID AND ALP1.PROPERTYNAME = 'Process2'
LEFT JOIN ASSEMBLYLINEPROPS ALP2 ON ALP2.HEADERSYSUNIQUEID = ASSEMBLYLINES.SYSUNIQUEID AND ALP2.PROPERTYNAME = 'Process3'
LEFT JOIN ASSEMBLYLINEPROPS ALP3 ON ALP3.HEADERSYSUNIQUEID = ASSEMBLYLINES.SYSUNIQUEID AND ALP3.PROPERTYNAME = 'Job Quantity'
LEFT JOIN ASSEMBLYLINEPROPS ALP4 ON ALP4.HEADERSYSUNIQUEID = ASSEMBLYLINES.SYSUNIQUEID AND ALP4.PROPERTYNAME = 'Drawing No'
WHERE ASSEMBLYLINES.ORDERNUMBER='16708R01'
AND ASSEMBLYLINES.LINECODE='FABPART'
AND ASSEMBLYLINES.SYSUSERCREATED <> 'EXTERNAL USER'
ORDER BY ALP4.PROPERTYVALUE
My results using the code above is:
Qty Total Qty
1 4
However, what I want is:
My results using the code above is:
Qty Total Qty
1 4
2 4
3 4
4 4
I understand the While loop would be something like:
While Qty <= ALP3.PROPERTYVALUE Do
<<output>>
Loop
Qty Total Qty
1 4
2 4
3 4
4 4
I understand the While loop would be something like:
While Qty <= ALP3.PROPERTYVALUE Do
<<output>>
Loop
So, your "quantity" column is not actually a quantity of some real data (like quantity of containers in cargo ship), but a row number in some your output report/grid.
And then what you want is limiting the output "rowset" - matrix, table, grid - to some N first rows.
Well, that is exactly how it is done, asking for the first rows only.
Select FIRST(4) column1, column2, column3
From table 1
Where condition1 and condition2 or condition3
See the "first" clause in documentation: https://www.firebirdsql.org/file/documentation/reference_manuals/fblangref25-en/html/fblangref25-dml-select.html
Also see "Limiting result rows" chapyer in Wikipedia: https://en.wikipedia.org/wiki/Select_%28SQL%29#Limiting_result_rows
You can also use "window functions" starting with Firebird version 3, but they are somewhat overkill for the simple task of "only give me first N rows".
Now, there is one more method that provides for embedding a totally voluntary condition, but that is from "ugly hacks" toolsets and does not work in a typical situation when several simultaneous connections form different client programs are running. You can use a "generator" as part of the WHILE clause:
Select .....
Where (GEN_ID(cancel_generator_name, 0) = 0) AND ( ...you normal conditions...)
You set the generator value to 0 before the query, and your client evaluates some conditions of your choice while reading the data, and when it wants to - from some another SQL command library object it issues the generator change command, which would immediately skip the rest of the query. However while sometimes this is a useful technique, but only in very specific rare situations.
Since Mark seems to be better guessing than me, then some outlines for future guesswork.
SP is a standard abbreviation for SQL Stored Procedure. Firebird's Execute Block is essentially an anonymous non-persistent SP.
So, we start with a persistent and named SP.
create or alter procedure SEQ (
FROM_1_TO integer not null)
returns (
COUNTER integer)
as
begin
counter = 1;
while ( counter <= from_1_to ) do begin
suspend;
counter = counter + 1;
end
end
Select 1, s.counter from rdb$database, seq(5) s
CONSTANT COUNTER
1 1
1 2
1 3
1 4
1 5
The next question would be how to
join the table with SP (stored procedure) dependent upon specific table row values
avoid SP being executed with NULL parameter values
The answer is - LEFT JOIN, as shown in the FAQ: http://www.firebirdfaq.org/faq143/
CREATE TABLE T2 (
ID INTEGER NOT NULL PRIMARY KEY,
TITLE VARCHAR(10) NOT NULL,
QTY INTEGER NOT NULL
);
INSERT INTO T2 (ID, TITLE, QTY) VALUES (1, 'aaaa', 2);
INSERT INTO T2 (ID, TITLE, QTY) VALUES (2, 'bbbb', 5);
INSERT INTO T2 (ID, TITLE, QTY) VALUES (3, 'ccccc', 4);
Select * from t2 t
left join seq(t.qty) s on 1=1
ID TITLE QTY COUNTER
1 aaaa 2 1
1 aaaa 2 2
2 bbbb 5 1
2 bbbb 5 2
2 bbbb 5 3
2 bbbb 5 4
2 bbbb 5 5
3 ccccc 4 1
3 ccccc 4 2
3 ccccc 4 3
3 ccccc 4 4
If you would have many different queries on different tables/fields that would require this rows-cloning added then having a dedicated counter-generating SP makes sense.
However if you only need this rather exotic rows cloning once, then maybe polluting a global namespace with an SP you would never need again would be less of a good idea.
It seems one can not select from an EB, though: Select from execute block?
So you would have to make a specific ad-hoc EB exactly for your select statement. Which, arguably, might be the very reason d'etre for anonymous non-persistent EB.
execute block
returns (ID INTEGER, TITLE VARCHAR(10), QTY INTEGER, COUNTER INTEGER)
as
begin
for select
id, title, qty from t2
into :id, :title, :qty
do begin
counter = 1;
while
(counter <= qty)
do begin
suspend;
counter = counter + 1;
end
end
end
However the data access library your application uses to connect to Firebird should understand then that while this query is not SELECT-query it still returns the "rowset". Usually they do, but who knows.
OrderId OrderCode Description
-------------------------------
1 Z123 Stuff
2 ABC999 Things
3 Z123 Stuff
I have duplicates in a table like the above. I'm trying to get a report of which Orders are duplicates, and what Order they are duplicates of, so I can figure out how they got into the database.
So ideally I'd like to get an output something like;
OrderId IsDuplicatedBy
-------------------------
1 3
3 1
I can't work out how to code this in SQL.
You can use the same table twice in one query and join on the fields you need to check against. T1.OrderID <> T2.OrderID is needed to not find a duplicate for the same row.
declare #T table (OrderID int, OrderCode varchar(10), Description varchar(50))
insert into #T values
(1, 'Z123', 'Stuff'),
(2, 'ABC999', 'Things'),
(3, 'Z123', 'Stuff')
select
T1.OrderID,
T2.OrderID as IsDuplicatedBy
from #T as T1
inner join #T as T2
on T1.OrderCode = T2.OrderCode and
T1.Description = T2.Description and
T1.OrderID <> T2.OrderID
Result:
OrderID IsDuplicatedBy
1 3
3 1
I need to be able to query a SharePoint database for survey results. The type of data I'm having problems with is a "Rating Scale" value. So the data in each table column represents a whole group of sub-questions and their answers.
So the following is an example of what is found in ONE column:
1. Our function has defined how Availability is measured the hardware/software in Production;#3#2. Availability threshold levels exist for our function (e.g., SLA's);#3#3. Our function follows a defined process when there are threshold breaches;#4#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#4#6. Operating Level Agreements (OLA's) guide our interaction with other internal teams;#4#
The Questions end with a semi-colon and their answers are inside the two # signs. So the answer to the first question is 3.
When I export the results of the survey it formats each question as a column header and the answer as the value in the cell below, which is ideal to get an average for each question, and would love to be able to replicate that from a SQL query.
But if I could get query results into two columns (Question, Answer)...I'd be thrilled with that.
Any help is appreciated.
Thanks very much
Hank Stallings
*****ADDENDUM:**
This was my version of astander's solution...THANKS again!
DECLARE #Table TABLE(
QuestionSource VARCHAR(50),
QA VARCHAR(5000)
)
DECLARE #ReturnTable TABLE(
QuestionSource VARCHAR(50),
Question VARCHAR(5000),
Answer int
)
DECLARE #XmlField XML,
#QuestionSource VARCHAR(50)
INSERT INTO #Table SELECT
'Availability' AS QuestionSource,CONVERT(varchar(5000),ntext1) FROM UserData WHERE tp_ContentType = 'My Survey'
INSERT INTO #Table SELECT
'Capacity' AS QuestionSource,CONVERT(varchar(5000),ntext2) FROM UserData WHERE tp_ContentType = 'My Survey'
--SELECT * FROM #Table
DECLARE Cur CURSOR FOR
SELECT QuestionSource,
CAST(Val AS XML) XmlVal
FROM (
SELECT QuestionSource,
LEFT(Vals, LEN(Vals) - LEN('<option><q>')) Val
FROM (
SELECT QuestionSource,
'<option><q>' + REPLACE(REPLACE(REPLACE(QA,'&','&'), ';#','</q><a>'), '#', '</a></option><option><q>') Vals
FROM #Table
) sub
) sub
OPEN Cur
FETCH NEXT FROM Cur INTO #QuestionSource,#XmlField
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #ReturnTable
SELECT #QuestionSource,
T.split.query('q').value('.', 'nvarchar(max)') question,
T.split.query('a').value('.', 'nvarchar(max)') answer
FROM #XmlField.nodes('/option') T(split)
FETCH NEXT FROM Cur INTO #QuestionSource,#XmlField
END
CLOSE Cur
DEALLOCATE Cur
SELECT * FROM #ReturnTable
You have to have a split function set up, but once you have it, try this cursor free solution:
I prefer the number table approach to split a string in TSQL
For this method to work, you need to do this one time table setup:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Once the Numbers table is set up, create this split function, which WILL return empty strings and row numbers:
CREATE FUNCTION [dbo].[FN_ListToTableRows]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN
(
----------------
--SINGLE QUERY-- --this WILL return empty rows
----------------
SELECT
ROW_NUMBER() OVER(ORDER BY number) AS RowNumber
,LTRIM(RTRIM(SUBSTRING(ListValue, number+1, CHARINDEX(#SplitOn, ListValue, number+1)-number - 1))) AS ListValue
FROM (
SELECT #SplitOn + #List + #SplitOn AS ListValue
) AS InnerQuery
INNER JOIN Numbers n ON n.Number < LEN(InnerQuery.ListValue)
WHERE SUBSTRING(ListValue, number, 1) = #SplitOn
);
GO
You can now easily split a CSV string into a table and join on it, NOTE this split function returns empty strings and row numbers:
select * from dbo.FN_ListToTableRows(',','1,2,3,,,4,5,6777,,,')
OUTPUT:
RowNumber ListValue
-------------------- ------------
1 1
2 2
3 3
4
5
6 4
7 5
8 6777
9
10
11
(11 row(s) affected)
Your can now use a CROSS APPLY to split every row in your table like:
DECLARE #YourTable table (RowID int, RowValue varchar(8000))
INSERT INTO #YourTable VALUES (1,'1. Our function has defined how Availability is measured the hardware/software in Production;#3#2. Availability threshold levels exist for our function (e.g., SLA''s);#3#3. Our function follows a defined process when there are threshold breaches;#4#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#4#6. Operating Level Agreements (OLA''s) guide our interaction with other internal teams;#4#')
INSERT INTO #YourTable VALUES (2,'1. one;#1#2. two;#2#3. three;#3#')
INSERT INTO #YourTable VALUES (3,'1. aaa;#1#2. bbb;#2#3. ccc;#3#')
;WITH AllRows As
(
SELECT
o.RowID,st.RowNumber,st.ListValue AS RowValue
FROM #YourTable o
CROSS APPLY dbo.FN_ListToTableRows('#',LEFT(o.RowValue,LEN(o.RowValue)-1)) AS st
)
SELECT
a.RowID,a.RowValue AS Question, b.RowValue AS Answer
FROM AllRows a
LEFT OUTER JOIN AllRows b ON a.RowID=b.RowID AND a.RowNumber+1=b.RowNumber
WHERE a.RowNumber % 2 = 1
OUTPUT:
RowID Question Answer
----------- ----------------------------------------------------------------------------------------------- -------
1 1. Our function has defined how Availability is measured the hardware/software in Production; 3
1 2. Availability threshold levels exist for our function (e.g., SLA's); 3
1 3. Our function follows a defined process when there are threshold breaches; 4
1 4. Our function collects and maintains Availability data; 4
1 5. Comparative analysis helps identify trending with the Availability data; 4
1 6. Operating Level Agreements (OLA's) guide our interaction with other internal teams; 4
2 1. one; 1
2 2. two; 2
2 3. three; 3
3 1. aaa; 1
3 2. bbb; 2
3 3. ccc; 3
(12 row(s) affected)
OK, let see. I had to use a cursor, as this would probably have been better achieved from a programming language like C#, but here goes... Using Sql Server 2005, try the following. Let me know if you need any explanations.
DECLARE #Table TABLE(
QuestionSource VARCHAR(50),
QA VARCHAR(1000)
)
DECLARE #ReturnTable TABLE(
QuestionSource VARCHAR(50),
Question VARCHAR(1000),
Answer VARCHAR(10)
)
DECLARE #XmlField XML,
#QuestionSource VARCHAR(40)
INSERT INTO #Table SELECT
'Availability','1. Our function has defined how Availability is measured the hardware/software in Production;#3#2. Availability threshold levels exist for our function (e.g., SLA''s);#3#3. Our function follows a defined process when there are threshold breaches;#4#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#4#6. Operating Level Agreements (OLA''s) guide our interaction with other internal teams;#4#'
INSERT INTO #Table SELECT
'Capacity', '1. Our function has defined how Availability is measured the hardware/software in Production;#1#2. Availability threshold levels exist for our function (e.g., SLA''s);#2#3. Our function follows a defined process when there are threshold breaches;#3#4. Our function collects and maintains Availability data;#4#5. Comparative analysis helps identify trending with the Availability data;#5#6. Operating Level Agreements (OLA''s) guide our interaction with other internal teams;#6#'
DECLARE Cur CURSOR FOR
SELECT QuestionSource,
CAST(Val AS XML) XmlVal
FROM (
SELECT QuestionSource,
LEFT(Vals, LEN(Vals) - LEN('<option><q>')) Val
FROM (
SELECT QuestionSource,
'<option><q>' + REPLACE(REPLACE(QA, ';#','</q><a>'), '#', '</a></option><option><q>') Vals
FROM #Table
) sub
) sub
OPEN Cur
FETCH NEXT FROM Cur INTO #QuestionSource, #XmlField
WHILE ##FETCH_STATUS = 0
BEGIN
INSERT INTO #ReturnTable
SELECT #QuestionSource,
T.split.query('q').value('.', 'nvarchar(max)') question,
T.split.query('a').value('.', 'nvarchar(max)') answer
FROM #XmlField.nodes('/option') T(split)
FETCH NEXT FROM Cur INTO #QuestionSource, #XmlField
END
CLOSE Cur
DEALLOCATE Cur
SELECT *
FROM #ReturnTable