How to get all Contract no against Leads in oracle sql query? - sql

I need to create a sql query for below scenario:
Table name is remark
Columns are contractno and leadid.
1 contractno can have multiple leadid.
similarly,
1 leadid can assigned to multiple contractno.
Lets assume:
C1 --> L1
C2 --> L1, L2
C3 --> L2
I will get only one contractno i.e. C1 as parameter.
Now I have to find all Contracts against C1 through leadid.
Please help me out how I can achieve this.
Thank you.

SELECT r1.contractno
FROM remark r1
JOIN remark r2
ON r1.leadid = r2.leadid
WHERE r2.contractno = 'C1'
AND r1.contractno <> 'C1'
This assume your table has this format:
contractno leadid
C1 L1
C2 L1
C2 L2
C3 L1
If you dont, then you need to split the csv value into rows first:
Turning a Comma Separated string into individual rows

You can use a LISTAGG if you have to group list of contracts. Here too it is assumed that your table has linear format and not comma separated leadids
WITH cn
AS (SELECT DISTINCT leadid
FROM remark
WHERE contractno = 'C1')
SELECT Listagg(r.contractno, ',')
within GROUP (ORDER BY ROWNUM) contractno_C1
FROM remark r
join cn
ON r.leadid = cn.leadid
WHERE r.contractno <> 'C1'
GROUP BY cn.leadid;
http://sqlfiddle.com/#!4/54e48/1/0

Related

Filter a join based on multiple rows

I'm trying to write a query that filters a join based on several rows in another table. Hard to put it into words, so I'll provide a cut-back simple example.
Parent
Child
P1
C1
P1
C2
P1
C3
P2
C1
P2
C2
P2
C4
P3
C1
P3
C3
P3
C5
Essentially all rows are stored in the same table, however there is a ParentID allowing one item to link to another (parent) row.
The stored procedure is taking a comma delimited list of "child" codes, and based on whatever is in this list, I need to provide a list of potential siblings.
For example, if the comma delimited list was empty, the returned rows should be C1, C2, C3, C4, C5. If the list is "C2", the returned rows would be C1, C3, C4, and if the list is 'C1, C2', then the only returned row would be c3, c4.
Sample query:
SELECT [S].[ID]
FROM utItem [P]
INNER JOIN utItem [C]
ON [P].[ID] = [C].[ParentID]
INNER JOIN
(
-- Encapsulated to simplify sample.
SELECT [ID]
FROM udfListToRows( #ChildList )
GROUP BY
[ID]
) [DT]
ON [DT].[ID] = [C].[ID]
/*
In the event where I passed in "C2", this would work, it would return C1, C3, C4.
However this falls apart the moment there is more than 1 value in #ChildList. If I pass in "C2, C3", it would return siblings for either. But I only want siblings of BOTH.
**/
INNER JOIN [utItem] [S]
ON [C].[ParentID] = [S].[ParentID]
AND [C].[ID] <> [S].[ID]
WHERE
#ChildList IS NOT NULL
GROUP BY
[S].[ID]
UNION ALL
-- In the event that no #ChildList values are provided, return a full list of possible children (e.g. 1,2,3,4,5).
SELECT [C].[ID]
FROM [utItem] [P]
INNER JOIN [utItem] [C]
ON [P].[ID] = [C].[ParentID]
WHERE
#ChildList IS NULL
GROUP BY
[C].[ID]
Firstly, you can split your data into a table variable for ease of use
DECLARE #input TABLE (NodeId varchar(2));
INSERT #input (NodeId)
SELECT [ID]
FROM udfListToRows( #ChildList ); -- or STRING_SPLIT or whatever
Assuming you already had your data in a proper table variable (rather than a comma-separated list) you can do this
DECLARE #totalCount int = (SELECT COUNT(*) FROM #input);
SELECT DISTINCT
t.Child
FROM (
SELECT
t.Parent,
t.Child,
i.NodeId,
COUNT(i.NodeId) OVER (PARTITION BY t.Parent) matches
FROM YourTable t
LEFT JOIN #input i ON i.NodeId = t.Child
) t
WHERE t.matches = #totalCount
AND t.NodeId IS NULL;
db<>fiddle
This is a kind of relational division
Left-join the input to the main table
Using a window function, calculate how many matches you get per Parent
There must be at least as many matches as there are inputs
We take the distinct Child, excluding the original inputs

Linked list concept in postgresql

I am new to postgresql, can you please guide me about my query listed below?
I have a table in postgres (database) named "app" having two columns "aid" and "cid".
Table Name: app
aid | cid
a1 | a3
a2 | null
a3 | a5
a4 | a6
a5 | null
a6 | null
What I want to display(using sql query in server), when I select "a1" or "a3" or "a5" from aid using sql query, I want to list all values associated with "a1" and its child cid (in this case I want an output = a1 a3 a5), its like a linked list a1 is connected to a3 and a3 is connected to a5.
If I select "a4" using sql query, I need an output like this("a4 a6")
You need to use recursion to accomplish this:
with recursive walk as (
select aid, cid, array[aid] as path
from app
union all
select w.aid, a.cid, w.path||a.aid
from walk w
join app a
on a.aid = w.cid
)
select *, array_to_string(path, ' ') as text_path
from walk
where cid is null;
Working fiddle here.
If your table is large, then to limit the cost of recursion, use a where clause in the top half of the walk CTE to restrict your starting point.
with recursive walk as (
select aid, cid, array[aid] as path
from app
where aid = 'a1'
union all
. . .
You can get the reverse path without having to recurse again like this:
with recursive walk as (
select aid, cid, array[aid] as path
from app
union all
select w.aid, a.cid, w.path||a.aid
from walk w
join app a
on a.aid = w.cid
), forward as (
select *, array_to_string(path, ' ') as text_path
from walk
where cid is null
), reverse as (
select distinct on (a.aid) a.aid, f.path, f.text_path, r.path as rpath
from app a
join forward f
on f.aid = a.aid
join forward r
on r.path #> array[a.aid]
order by a.aid, array_length(r.path, 1) desc
)
select r.aid, r.path, r.text_path,
array_agg(u.rid order by u.rn desc) as up_path,
string_agg(u.rid, ' ' order by u.rn desc) as text_up_path
from reverse r
join lateral unnest(rpath)
with ordinality as u(rid, rn)
on u.rn <= array_position(r.rpath, r.aid)
group by r.aid, r.path, r.text_path;
Updated fiddle.

Group by on two tables and perform Left join on outcome VBA ADODB SQL Query

I want to perform Group BY on two csv files and then perform Left Join on the outcome of both tables through VBA ADO Query in Excel. My end motive is to print the recordset.
Here is what I have done so far.
SELECT * FROM (
SELECT f1.[c3],
f1.[c4],
f1.[c5],
f1.[c6],
Sum(f1.[c8]) AS SUMDATA
FROM test1.csv F1
GROUP BY f1.[c3],
f1.[c4],
f1.[c5],
f1.[c6]) AS f3
LEFT JOIN SELECT * FROM (
SELECT f2.[c3],
f2.[c4],
f2.[c5],
f2.[c6],
Sum(f2.[c8]) AS SUMDATA
FROM test2.csv f2
GROUP BY f2.[c3],
f2.[c4],
f2.[c5],
f2.[c6]) AS f4
on f3.[c3]+ f3.[c4]+ f3.[c5]+ f3.[c6] = f4.[c3]+ f4.[c4]+ f4.[c5]+ f4.[c6]
WHERE f3.[SUMDATA] <> f4.[SUMDATA]
This shows a syntax error. How to implement this? Any help is much appreciated. TIA.
An update -
I manage to implement 1 LEFT JOIN and 2 GROUP BYs between 2 tables. As per the request, here are few details regarding my dataset.
It consists of fields - c1, c2 .... c8.
c8 is my target field.
My expected output - I do not need c7, c1 and c2 in output sheet. The info of c7, c1 and c2 is irrelevant. I need to do 5 things with my data.
Group Sum the c8 field based on c3, c4, c5 and c6 fields in CSV file 1 and store target field as SUMDATA
Group Sum the c8 field based on c3, c4, c5 and c6 fields in CSV file 2 and store target field as SUMDATA
Find the mismatched SUMDATA field entries between CSV1 and CSV2 (I used LEFT JOIN for this on concatenated c3, c4, c5, c6 fields)
Find the entries which are present in CSV1 but not in CSV2
Find the entries which are present in CSV2 but not in CSV1
Currently, I manage to write the code that works till step 3. I need to store the grouped tables temporarily which I got from Step 1 and 2, to perform the steps 4 and 5 which can be done through 2 more UNION, LEFT JOINs, and WHERE combination. This is where I am stuck right now.
This isn't really an answer but the formatting is important for readability.
It looks like there's a lot wrong with your SQL.
The syntax should look like this (assuming querying a csv works like you are thinking):
SELECT SUB1.Field1,
SUB1.AggField AS Agg1,
SUB2.AggField AS Agg2
FROM (SELECT Field1,
MAX(Field2) Agg_Field
FROM Table1 T1
GROUP
BY Field1
) SUB1
LEFT
JOIN (SELECT Field1,
MAX(Field2) Agg_Field
FROM Table1 T2
GROUP
BY Field1
) SUB2
ON SUB1.Field1 = SUB2.Field1
WHERE SUB1.AggField <> SUB2.AggField;
Also, you are missing a comma here: F1.[c5] F1.[c6] in the first chunk.
Try fixing the SQL syntax like this and see where that gets you.

GROUP BY without the key in the resulting bag

I have:
a b
a c
a d
and I would like to generate:
a, {(b),(c),(d)}
Doing this by using GROUP results in:
a, {(a,b),(a,c),(a,d)}
How do I get rid of the first field in the bag?
Thanks.
There is no option to do this in GROUP. You'll have to project that column out in a FOREACH.
-- DESCRIBE A ;
-- A: {c1: chararray, c2: chararray}
-- DUMP A ;
-- a b
-- a c
-- a d
B = GROUP A BY c1 ;
C = FOREACH B GENERATE group AS c1, A.c2 AS grpd_c2 ;
In cases where I have to do this I generally use this way for brevity:
D = FOREACH (GROUP A BY c1)
GENERATE group AS c1, A.c2 AS grpd_c2 ;
(Also, this way helps to remind me to not to use B.c2)
The key is A.c2 which returns a bag with only the c2 column from the original bag. If, for example, you had 3 fields (c1, c2, c3) you would use A.(c2, c3) instead.
B = GROUP A BY c1 ;
If you have more fields, it will be something like this:
C = FOREACH B GENERATE group AS c1, A.(c2,....);

How to optimize a Database Model for a M:N relationship

Edit 10-Apr-2013
In order to make myself clear I am adding another (simplified) example showing the principle of what I am trying to achieve:
T1 - PERSONHAS T2 - PRODUCTNEED
ANTON has WHEEL CAR need ENGINE
ANTON has ENGINE CAR need WHEEL
ANTON has NEEDLE SHIRT need NEEDLE
BERTA has NEEDLE SHIRT need THREAD
BERTA has THREAD JAM need FRUIT
BERTA has ENGINE JAM need SUGAR
Q3 - PERSONCANMAKE
ANTON canmake CAR
BERTA canmake SHIRT
Q4 - PERSONCANNOTMAKE
ANTON cannotmake SHIRT
ANTON cannotmake FRUIT
BERTA cannotmake CAR
BERTA cannotmake FRUIT
I have T1 and T2 and want to create queries for Q3 and Q4
End Edit 10-Apr-2013
Preface:
In order to create a product (P) I need to have certain generic capabilities (C - like a factory, supply, electricity, water, etc.)
A product manager defines all generic capabilities needed to create his/her product.
In a location (L) I have certain generic capabilities (C)
A location manager defines the capabilities his/her location is able to provide. This could be a clear YES, a clear NO, or the location manager does not list a certain capability at all.
DB Model:
I have created the following root entities
Location (PK: L) - values L1, L2, L3 // in real ca. 250 rows of L
Product (PK: P) - values P1, P2 // in real ca. 150 rows of P
Capability (PK: C) - values C1, C2, C3 // in real ca. 80 rows of C
and the following child (dependent) entities
ProductCapabilityAssignment:P, C (PK: P, C, FK: P, C)
P1 C1
P1 C2
P2 C1
P2 C3
LocationCapabilityAssignment: L, C, Status (Y/N) (PK: L, C, FK: L, C)
L1 C1 Y
L2 C1 Y
L2 C2 Y
L2 C3 N
L3 C1 Y
L3 C2 Y
L3 C3 Y
Task:
The task is to find out whether a certain product can be produced at a certain location, whereby all capabilities defined for the product must be present at that location. In order to answer this I couldn't help myself but to
create a Cartesian Product of Location and ProductCapabilityAssignment (CL_Cart) to ensure that for each location I am listing all possible products with their cpability needs
CREATE VIEW CL_Cart AS
SELECT L.L, PCA.P, PCA.C
FROM Location AS L, ProductCapabilityAssignment AS PCA;
create an outer join between CL_Cart and LocationCapabilityAssignment to match in all capabilities a location can provide
CREATE VIEW Can_Produce AS
SELECT X.L, X.P, X.C, LCA.Status
FROM CL_CArt AS X LEFT JOIN LocationCapabilityAssignment AS LCA ON (X.C = LCA.C) AND (X.L = LCA.L);
so that finaly I get
SELECT L, P, C, Status
FROM Can_Produce;
L1 P1 C1 Y
L1 P1 C2 NULL // C2 not listed for L1
L1 P2 C1 Y
L1 P2 C3 NULL // C3 not listed for L1
L2 P1 C1 Y
L2 P1 C2 Y
L2 P2 C1 Y
L2 P2 C3 N // C3 listed as "No" for L2
L3 P1 C1 Y
L3 P1 C2 Y
L3 P2 C1 Y
L3 P2 C3 Y
meaning that L1 cannot produce neither P1 nor P2, L2 can produce P1, and L3 can produce both P1, P2.
So I can query Can_Produce for a specific product/location and see what I have and what I don't have in terms of capabilities. I also can provide a shortcut overall YES/NO answer by examining Status="N" OR Status is NULL - if so the product cannot be produced.
Question:
For a relational database like MSSQL, MySQL, Oracle (not yet decided and beyond my influence) I am wondering if I have chosen the correct data model for this M:N relationship or if I could do any better. In particular I fear that with ca. 250 locations, 150 products and one product in average being defined by +/- 10 capabilities, so to say a Cartesian product of 375.000 rows, that performance will collapse due to huge memory consumption.
I would also really like to avoid stored procedures.
Any thoughts would be welcome.
--Environment Variables
Declare #Parent table (id int identity(1,1) primary key, Name varchar(20))
Declare #Components table (id int identity(1,1) primary key, Name varchar(20)) Insert into #Components (Name) values ('Engine'),('Wheel'),('Chassis'),('NEEDLE'),('THREAD'),('FRUIT'),('SUGAR')
Declare #Objects table (id int identity(1,1) primary key, Name varchar(20))
Declare #Template table (id int identity(1,1) primary key, Name varchar(20), ObjectID int, ComponentID int)
Insert into #Template (Name, ObjectID, ComponentID)
Select 'Vehicle', O.ID, C.ID from #Objects O, #Components C where O.Name = 'Car' and C.Name in ('Engine','Wheel','Chassis')union
Select 'Clothing', O.ID, C.ID from #Objects O, #Components C where O.Name = 'Shirt' and C.Name in ('Needle','Thread') union
Select 'Confectionary', O.ID, C.ID from #Objects O, #Components C where O.Name = 'JAM' and C.Name in ('FRUIT','SUGAR')
Declare #AvailableMaterials table (id int identity(1,1) primary key, TestType varchar(20), ParentID int, ComponentID int)
--Test Data
Insert into #AvailableMaterials (TestType,ParentID,ComponentID)
Select 'CompleteSet', P.ID, T.ComponentID from #Parent P, #Template T where P.Name = 'Driver' and T.Objectid = (Select ID from #Objects where Name = 'Car') union
Select 'CompleteSet', P.ID, T.ComponentID from #Parent P, #Template T where P.Name = 'Seamstress' and T.Objectid = (Select ID from #Objects where Name = 'Shirt') union
Select 'IncompleteSet', P.ID, T.ComponentID from #Parent P, #Template T where P.Name = 'Confectionarist' and T.Objectid = (Select ID from #Objects where Name = 'Jam')
and T.ComponentID not in (Select ID from #Components where Name = 'FRUIT')
--/*What sets are incomplete?*/
Select *
from #AvailableMaterials
where ID in (
Select SRCData.ID
from #AvailableMaterials SRCData cross apply (Select ObjectID from #Template T where ComponentID = SRCData.ComponentID) ObjectsMatchingComponents
inner join #Template T
on SRCData.ComponentID = T.ComponentID
and T.ObjectID = ObjectsMatchingComponents.ObjectID
cross apply (Select ObjectID, ComponentID from #Template FullTemplate where FullTemplate.ObjectID = T.ObjectID and FullTemplate.ComponentID not in (Select ComponentID from #AvailableMaterials SRC where SRC.ComponentID = FullTemplate.ComponentID)) FullTemplate
)
/*What sets are complete?*/
Select *
from #AvailableMaterials
where ID not in (
Select SRCData.ID
from #AvailableMaterials SRCData cross apply (Select ObjectID from #Template T where ComponentID = SRCData.ComponentID) ObjectsMatchingComponents
inner join #Template T
on SRCData.ComponentID = T.ComponentID
and T.ObjectID = ObjectsMatchingComponents.ObjectID
cross apply (Select ObjectID, ComponentID from #Template FullTemplate where FullTemplate.ObjectID = T.ObjectID and FullTemplate.ComponentID not in (Select ComponentID from #AvailableMaterials SRC where SRC.ComponentID = FullTemplate.ComponentID)) FullTemplate
)
Hi
This is the best I can come up with... It works on the premise that you have to know what the complete set is, to know what's missing. Once you have what's missing, you can tell the complete sets from the incomplete sets.
I doubt this solution will scale well, even if moved to #tables with indexing. Possibly though...
I too would be interested in seeing a cleaner approach. The above solution was developed in a SQL 2012 version. Note cross apply which limits the Cartesian effect somewhat.
Hope this helps.
I'm not sure what database you are using, but here is an example that would work in sql server - shouldn't require many changes to work in other databases...
WITH ProductLocation
AS
(
SELECT P.P,
P.Name as ProductName,
L.L,
L.Name as LocationName
FROM Product P
CROSS
JOIN Location L
),
ProductLocationCapability
AS
(
SELECT PL.P,
PL.ProductName,
PL.L,
PL.LocationName,
SUM(PC.C) AS RequiredCapabilities,
SUM(CASE WHEN LC.L IS NULL THEN 0 ELSE 1 END) AS FoundCapabilities
FROM ProductLocation PL
JOIN ProductCapabilityAssignment PC
ON PC.P = PL.P
LEFT
JOIN LocationCapabilityAssignment LC
ON LC.L = PL.L
AND LC.C = PC.C
GROUP BY PL.P, PL.ProductName, PL.L, PL.LocationName
)
SELECT PLC.P,
PLC.ProductName,
PLC.L,
PLC.LocationName,
CASE WHEN PLC.RequiredCapabilities = PLC.FoundCapabilities THEN 'Y' ELSE 'N' END AS CanMake
FROM ProductLocationCapability PLC
(Not sure if the field names are correct, I couldn't quite make sense of the schema description!)