I have one select statement with multiple full joins in it.
select
aroll as aroll,
aname as aname,
astandard as astandard,
amarks as amarks,
adepartment_id as adepartment_id,
broll as broll,
bname as bname,
bstandard as bstandard,
bmarks as bmarks,
bdepartment_id as bdepartment_id,
croll as croll,
cname as cname,
cstandard as cstandard,
cmarks as cmarks,
cdepartment_id as cdepartment_id
from
(
(
(
select
roll as aroll,
name as aname,
standard as astandard,
marks as amarks,
department_id as adepartment_id
from student
where department_id = 1
and standard = 10) as firstA
full join
(select
roll as broll,
name as bname,
standard as bstandard,
marks as bmarks,
department_id as bdepartment_id
from student
where department_id = 2
and standard = 10) as secondB
on
firstA.astandard = secondB.bstandard
) first_second_combined
full join
(select
roll as croll,
name as cname,
standard as cstandard,
marks as cmarks,
department_id as cdepartment_id
from student
where department_id = 3
and standard = 10) thirdC
on
first_second_combined.astandard = thirdC.cstandard
and
first_second_combined.bstandard = thirdC.cstandard
)x;
It gives me error as below
Error: FULL JOIN is only supported with merge-joinable or hash-joinable join conditions
You are making it look very complicated. You could write your query like this:
select
firstA.roll as aroll,
firstA.name as aname,
firstA.standard as astandard,
firstA.marks as amarks,
firstA.department_id as adepartment_id,
secondB.roll as broll,
secondB.name as bname,
secondB.standard as bstandard,
secondB.marks as bmarks,
secondB.department_id as bdepartment_id,
thirdC.roll as croll,
thirdC.name as cname,
thirdC.standard as cstandard,
thirdC.marks as cmarks,
thirdC.department_id as cdepartment_id
from student firstA
full join student secondB ON firstA.standard = secondb.standard
full join student thirdC ON firstA.standard = thirdC.standard
It essentially means "Get the triplets of students sharing a common standard.".
Note that you will get a triplet (S, S, S) for each student S.
Also, for each triplet (A, B, C), you will also get `(A, C, B), (B, A, C), (B, C, A), (C, A, B), (C, B, A). If your intent is to get only triplets of different students, and also to avoid repetitions, you may want to add :
where firstA.name < secondB.name AND secondB.name < thirdC.name
Note also that using FULL JOIN here makes sense only if you have NULL values in the standard column. Otherwise you could aswell use INNER JOINs.
I hope this helps you get better knowledge of how SQL works.
Related
I am trying to parse email files that will be coming periodically for data that is contained within. We plan to setup cfmail to get the email within the box within CF Admin to run every minute.
The data within the email consists of name, code name, address, description, etc. and will have consistent labels so we are thinking of performing a loop or find function for each field of data. Would that be a good start?
Here is an example of email data:
INCIDENT # 12345
LONG TERM SYS# C12345
REPORTED: 08:39:34 05/20/19 Nature: FD NEED Address: 12345 N TEST LN
City: Testville
Responding Units: T12
Cross Streets: Intersection of: N Test LN & W TEST LN
Lat= 39.587453 Lon= -86.485021
Comments: This is a test post. Please disregard
Here's a picture of what the data actually looks like:
So we would like to extract the following:
INCIDENT
LONG TERM SYS#
REPORTED
Nature
Address
City
Responding Units
Cross Streets
Comments
Any feedback or suggestions would be greatly appreciated!
Someone posted this but it was apparently deleted. Whoever it was I want to thank you VERY MUCH as it worked perfectly!!!!
Here is the function:
<!---CREATE FUNCTION [tvf-Str-Extract] (#String varchar(max),#Delimiter1
varchar(100),#Delimiter2 varchar(100))
Returns Table
As
Return (
with cte1(N) as (Select 1 From (values(1),(1),(1),(1),(1),(1),(1),(1),(1),(1))
N(N)),
cte2(N) as (Select Top (IsNull(DataLength(#String),0)) Row_Number() over (Order By
(Select NULL)) From (Select N=1 From cte1 N1,cte1 N2,cte1 N3,cte1 N4,cte1 N5,cte1 N6) A
),
cte3(N) as (Select 1 Union All Select t.N+DataLength(#Delimiter1) From cte2 t
Where Substring(#String,t.N,DataLength(#Delimiter1)) = #Delimiter1),
cte4(N,L) as (Select S.N,IsNull(NullIf(CharIndex(#Delimiter1,#String,s.N),0)-
S.N,8000) From cte3 S)
Select RetSeq = Row_Number() over (Order By N)
,RetPos = N
,RetVal = left(RetVal,charindex(#Delimiter2,RetVal)-1)
From ( Select *,RetVal = Substring(#String, N, L) From cte4 ) A
Where charindex(#Delimiter2,RetVal)>1
)
And here is the CF code that worked:
<cfquery name="body" datasource="#Application.dsn#">
Declare #S varchar(max) ='
INCIDENT 12345
LONG TERM SYS C12345
REPORTED: 08:39:34 05/20/19 Nature: FD NEED Address: 12345 N TEST
LN City: Testville
Responding Units: T12
Cross Streets: Intersection of: N Test LN & W TEST LN
Lat= 39.587453 Lon= -86.485021
Comments: This is a test post. Please disregard
'
Select Incident = ltrim(rtrim(B.RetVal))
,LongTerm = ltrim(rtrim(C.RetVal))
,Reported = ltrim(rtrim(D.RetVal))
,Nature = ltrim(rtrim(E.RetVal))
,Address = ltrim(rtrim(F.RetVal))
,City = ltrim(rtrim(G.RetVal))
,RespUnit = ltrim(rtrim(H.RetVal))
,CrossStr = ltrim(rtrim(I.RetVal))
,Comments = ltrim(rtrim(J.RetVal))
From (values (replace(replace(#S,char(10),''),char(13),' ')) )A(S)
Outer Apply [dbo].[tvf-Str-Extract](S,'INCIDENT' ,'LONG
TERM' ) B
Outer Apply [dbo].[tvf-Str-Extract](S,'LONG TERM SYS'
,'REPORTED' ) C
Outer Apply [dbo].[tvf-Str-Extract](S,'REPORTED:' ,'Nature'
) D
Outer Apply [dbo].[tvf-Str-Extract](S,'Nature:'
,'Address' ) E
Outer Apply [dbo].[tvf-Str-Extract](S,'Address:' ,'City'
) F
Outer Apply [dbo].[tvf-Str-Extract](S,'City:'
,'Responding ') G
Outer Apply [dbo].[tvf-Str-Extract](S,'Responding Units:','Cross'
) H
Outer Apply [dbo].[tvf-Str-Extract](S,'Cross Streets:' ,'Lat'
) I
Outer Apply [dbo].[tvf-Str-Extract](S+'|||','Comments:' ,'|||'
) J
</cfquery>
<cfoutput>
B. #body.Incident#<br>
C. #body.LongTerm#<br>
D. #body.Reported#<br>
SQL tends to have limited string functions, so it isn't the best tool for parsing. If the email content is always in that exact format, you could use either plain string functions or regular expressions to parse it. However, the latter is more flexible.
I suspect the content actually does contain new lines, which would make for simpler parsing. However, if you prefer searching for content in between two labels, regular expressions would do the trick.
Build an array of the label names (only). Loop through the array, grabbing a pair of labels: "current" and "next". Use the two values in a regular expression to extract the text in between them:
label &"\s*[##:=](.*?)"& nextLabel
/* Explanation: */
label - First label name (example: "Incident")
\s* - Zero or more spaces
[##:=] - Any of these characters: pound sign, colon or equal sign
(.*?) - Group of zero or more characters (non-greedy)
nextLabel - Next label (example: "Long Term Sys")
Use reFindNoCase() to get details about the position and length of matched text. Then use those values in conjunction with mid() to extract the text.
Note, newer versions like ColdFusion 2016+ automagically extract the text under a key named MATCH
The newer CF2016+ syntax is slicker, but something along these lines works under CF10:
emailBody = "INCIDENT # 12345 ... etc.... ";
labelArray = ["Incident", "Long Term Sys", "Reported", ..., "Comments" ];
for (pos = 1; pos <= arrayLen(labelArray); pos++) {
// get current and next label
hasNext = pos < arrayLen(labelArray);
currLabel = labelArray[ pos ];
nextLabel = (hasNext ? labelArray[ pos+1 ] : "$");
// extract label and value
matches = reFindNoCase( currLabel &"\s*[##:=](.*?)"& nextLabel, emailBody, 1, true);
if (arrayLen(matches.len) >= 2) {
results[ currLabel ] = mid( emailBody, matches.pos[2], matches.len[2]);
}
}
writeDump( results );
Results:
I'm stuck with this query. i want to show a field called "Executive" that must say "sin asignar" if there's no match when joining on the table 'prospectousuario', and if it exist it must say the first and the last name of the executive. But it returns the following error:
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an exp
The query is:
select p.IDCANCAP,
p.CEDULA,
p.NOMBRES,
p.APELLIDOS,
p.CELULAR,
p.CASA,
p.CORREO,
p.ESTABLECIMIENTO,
c.DESCRIPCION,
(select case when
p.CEDULA = pu.IDPROSPECTO and pu.IDUSUARIO = u.CEDULA
then u.NOMBRES+' '+u.APELLIDOS else 'Sin asignar'
end from usuario as u, PROSPECTOUSUARIO as pu) as Ejecutivo,
p.FECHA_CREAC
from PROSPECTO p, CANALCAPTACION c
where p.IDCANCAP = c.IDCANAL
Boy this is a guess, based on your SQL and a lot of reading between the lines... but give it a try:
select p.IDCANCAP, p.CEDULA, p.NOMBRES,
p.APELLIDOS, p.CELULAR,p.CASA, p.CORREO,
p.ESTABLECIMIENTO, c.DESCRIPCION, p.FECHA_CREAC,
case when exists
(Select * from PROSPECTOUSUARIO
Where IDPROSPECTO = p.CEDULA)
then u.NOMBRES+' '+ u.APELLIDOS
else 'Sin asignar' end Ejecutivo
from PROSPECTO p
join CANALCAPTACION c on c.IDCANAL = p.IDCANCAP
join usuario u on u.CEDULA = p.CEDULA
Just as the error message says, your one and only subquery
(select case when
p.CEDULA = pu.IDPROSPECTO and pu.IDUSUARIO = u.CEDULA
then u.NOMBRES+' '+u.APELLIDOS else 'Sin asignar'
end from usuario as u, PROSPECTOUSUARIO as pu)
returns more than 1 row, which is not allowed when used as an expression inside the SELECT. In fact, it appears that the u and pu tables are not joined against any other tables in any meaningful way.
To fix, you must join the tables. In fact, you don't even need a subquery for what you're trying to do - move all tables to the FROM section and join them properly, whether inner or outer. Unfortunately, I don't know which fields they should be joined on, so I can't help there without more information or wild guessing.
I have following view which is working but not sure how to add 2 tables to join.
This table is adres1 and it will join on the IDENT# and IDSFX# to table
prodta.adres1 called adent# and adsfx#, there I need a col. ads15.
then i also need to get the ship to, row in this adres1. this we get first from the order table, prodta. oeord1 in col. odgrc#. This grc# is 11 pos and is combined 8 and 3 of the ent and suf. these 2 represent the ship to record and looking in same table adres1 (we do have many logical views on them if it's easier, like adres15) we can get col. ADSTTC for the ship to state.
Not sure if can included these 2 new parts to the current view created code below. Please ask if something not clear, it's an old system and somewhat developed convoluted.
CREATE VIEW Prolib.SHPWEIGHTP AS SELECT
T01.IDORD#,
T01.IDDOCD,
T01.IDPRT#,
t01.idsfx#,
T01.IDSHP#,
T01.IDNTU$,
T01.IDENT#,
(T01.IDNTU$ * T01.IDSHP#) AS LINTOT,
T02.IAPTWT,
T02.IARCC3,
T02.IAPRLC,
T03.PHVIAC,
T03.PHORD#,
PHSFX#,
T01.IDORDT,
T01.IDHCD3
FROM PRODTA.OEINDLID T01
INNER JOIN PRODTA.ICPRTMIA T02 ON T01.IDPRT# = T02.IAPRT#
INNER JOIN
(SELECT DISTINCT
PHORD#,
PHSFX#,
PHVIAC,
PHWGHT
FROM proccdta.pshippf) AS T03 ON t01.idord# = T03.phord#
WHERE T01.IDHCD3 IN ('MDL','TRP')
I'm not exactly clear on what you're asking, and it looks like some of the column-names are missing from your description, but this should get you pretty close:
CREATE VIEW Prolib.SHPWEIGHTP AS
SELECT T01.IDORD#,
T01.IDDOCD,
T01.IDPRT#,
t01.idsfx#,
T01.IDSHP#,
T01.IDNTU$,
T01.IDENT#,
( T01.IDNTU$ * T01.IDSHP# ) AS LINTOT ,
T02.IAPTWT,
T02.IARCC3,
T02.IAPRLC,
T03.PHVIAC,
T03.PHORD#,PHSFX#,
T01.IDORDT,
T01.IDHCD3,
t04.ads15
FROM PRODTA.OEINDLID T01
INNER JOIN PRODTA.ICPRTMIA T02
ON T01.IDPRT# = T02.IAPRT#
INNER JOIN (SELECT DISTINCT
PHORD#,
PHSFX#,
PHVIAC,
PHWGHT
FROM proccdta.pshippf) AS T03
ON t01.idord# = T03.phord#
JOIN prodta.adres1 as t04
on t04.adent# = t01.adent#
and t04.adsfx# = t01.adsfx#
JOIN prodta.oeord1 t05
on t05.odgrc# = T01.IDENT# || T01.SUFFIX
WHERE T01.IDHCD3 IN ('MDL','TRP')
Let me know if you need more details.
HTH !
I have two tables:
1,'hello'
2,'world'
4,'this'
and
1,'john'
3,'king'
and I want to produce a table
1,'hello','john'
2,'world',''
3,'' ,king
4,'this' ,''
I am currently using the Pig command:
JOIN A BY code FULL OUTER,
B BY code;
but this gives me the output:
1,'hello',1,'john'
2,'world',,''
,'' ,3,king
4,'this' ,,''
I need the code columns to combine, how can I do this? Thanks
Yes join will always produce output like this,its a expected behavior in pig. One option could be try group operator instead of join operator.
a.txt
1,'hello'
2,'world'
4,'this'
b.txt
1,'john'
3,'king'
PigScript:
A = LOAD 'a.txt' USING PigStorage(',') AS (code:int,name:chararray);
B = LOAD 'b.txt' USING PigStorage(',') AS (code:int,name:chararray);
C = GROUP A BY code,B BY code;
D = FOREACH C GENERATE group,(IsEmpty(A.name) ? TOTUPLE('') : BagToTuple(A.name)) AS aname,(IsEmpty(B.name) ? TOTUPLE('') : BagToTuple(B.name)) AS bname;
E = FOREACH D GENERATE group,FLATTEN(aname),FLATTEN(bname);
DUMP E;
Output:
(1,'hello','john')
(2,'world',)
(3,,'king')
(4,'this',)
BagToTuple() is not available in native pig, you have to download the pig-0.11.0.jar and set it in your classpath.
Download jar from this link:
http://www.java2s.com/Code/Jar/p/Downloadpig0110jar.htm
A = load 'a' using PigStorage(',') as (code:int,name:chararray);
B = load 'b' using PigStorage(',') as (code:int,name:chararray);
C = join A by code full outer ,B by code;
D = foreach C generate
(A::code IS NULL ? B::code : A::code) AS code,
A::name as aname, B::name as bname;
dump D;
the result is
(1,'hello','john')
(2,'world',)
(3,,'king')
(4,'this,)
You can use union and then do a groupBy
Union A,B will give you:
1,'hello'
2,'world'
4,'this'
1,'john'
3,'king'
Now do a groupBy based on id. This will give you:
1, {'hello', 'john'}
2, {'world'}
3, {'king'}
4, {'this'}
Now you just need a udf to parse the bag. In udf iterate over each key to generate output in your format.
I also ran into same issue. This is how I solved it.
You can use the ternary operator after your join to re-assign a new code, based on whether it was populated in the A or B relations. In this example, if A.code is null then B.code is used, else A.code is used.
C = JOIN A BY code FULL OUTER, B BY code;
D = FOREACH C GENERATE
(A.code IS NULL ? B.code : A.code) AS code,
A.field1,
A.field2,
B.field3,
B.field4;
Please pardon me if this question has been asked before, but I simply don't have enough vocabulary to search for what I need as a novice in data bases.
I am using SQL server 2008.
I have a table tblPDCDetails with several columns. One of the columns PDCof holds values :
"A"(for applicant),
"C" for coapplicant,
"G" (for Guarantor).
Another column HolderID holds uniqueid (of holder).
The PDCHolders reside in their respective tables: Applicants in tblApplBasicDetails, CoApllicants in their own table and so on.
Now what I need is how should I retrive the names of holders from their respective tables, depending on the value in PDCof column.
Can I do it at all?
If no how should I work around this?
This should do:
SELECT A.*,
COALESCE(B.Name,C.Name,D.Name) Name
FROM dbo.tblPDCDetails A
LEFT JOIN dbo.tblApplBasicDetails B
ON A.HolderID = B.HolderID
AND A.PDCof = 'A'
LEFT JOIN dbo.tblCoApplBasicDetails C
ON A.HolderID = C.HolderID
AND A.PDCof = 'C'
LEFT JOIN dbo.tblGuarantorlBasicDetails D
ON A.HolderID = D.HolderID
AND A.PDCof = 'G'
The other option is to use a case switch:
Select case Main.PDCof
when 'A' then (select HolderID from Applicants where main.value = value)
when 'C' then (select HolderID from CoApplicants where main.value = value)
when 'G' then (select HolderID from Guarantor where main.value = value)
end
,main.*
from tblPDCDetails main
Depends on whether you run this a few times a day, or a few thousand times an hour