views:

431

answers:

6

Hi I am fairly new to SQL - so be gentle with me!

I basically have two tables that I am trying to join where the primary key in the first table is used multiple times in the second table (and forms the primary key in the second table along with another column)

It's probably best if I provide an example:

Table 1 : TravelRecords

TravelEventID  EventType  RecordLocator  OfficeID    
 0001            F          ABC123         LDN  
 0002            F          ABC234         LDN  
 0003            T          BDF232         SOU  
 0004            F          DFD890         GLA

Table 2 : TravelRecordRmks

TravelEventID  RemarkNo    Keyword       Text  
 0001            1           TVL          LOWCOST BOOKING  
 0001            2           TVL          CREDIT CARD USED  
 0001            3           PSG          COST CENTRE REQUIRED  
 0001            4           PSG          EMPLOYEE NUM REQUIRED
 0002            1           TVL          CREDIT CARD USED
 0002            2           AGT          BOOKED BY AGENT
 0002            3           AGT          CONFIRM WITH AIRLINE
 0002            4           TVL          LOWEST FARE CONFIRMED
 0002            5           TVL          NO CANCELLATION CHARGE
 0003            1           TVL          LOWCOST BOOKING
 0003            2           TVL          CARRIER : EASYJET
 0003            3           TVL          LOWEST FARE CONFIRMED
 0004            1           TVL          LOWCOST BOOKING
 0004            2           TVL          CREDIT CARD USED

For the second table the key is the combination of the TravelEventID and the RemarkNo which combined gives a unique ID.

Basically all I am trying to do is join the tables together and return the record locator for bookings that have a remark text line of LOWCOST BOOKING AND CREDIT CARD USED (so in the example above only ABC123 and DFD890 should be returned.

I've tried something along the lines of :

SELECT  TravelRecords.RecordLocator
FROM    TravelRecords INNER JOIN
        TravelRecordRmks ON TravelRecords.TravelEventID = TravelRecordRmks.TravelEventID db  
WHERE   (TravelRecordRmks.RemarkText = 'LOWCOST BOOKING') 
        and (TravelRecordRmks.RemarkText = 'CREDIT CARD USED')
ORDER BY dbo.vw_gTravelOrderEvent.RecordLocator

However - that returns nothing. This is probably really simple - but I can't get it to return the required response when I am looking for the single TravelEventID to contain both Remark Text fields.

Any help much appreciated Cheers

+5  A: 

The most readable way to check this is probably a double where exists, like:

SELECT  tr.*
FROM    TravelRecords tr
WHERE EXISTS (
    SELECT * FROM TravelRecordRmks trr 
    WHERE trr.TravelEventID = tr.TravelEventID
    AND trr.RemarkText = 'LOWCOST BOOKING'
)
AND EXISTS (
    SELECT * FROM TravelRecordRmks trr 
    WHERE trr.TravelEventID = tr.TravelEventID
    AND trr.RemarkText = 'CREDIT CARD USED'
)

An alternative that might perform better, using an inner join subquery as a filter:

SELECT  tr.*
FROM    TravelRecords tr
INNER JOIN (
        SELECT TravelEventID
        FROM TravelRecordRmks trr 
        WHERE RemarkText IN ('CREDIT CARD USED','LOWCOST BOOKING')
        GROUP BY TravelEventID
        HAVING COUNT(DISTINCT RemarkText) = 2
) filter 
ON      filter.TravelEventID = tr.TravelEventID

The HAVING COUNT(DISTINCT RemarkText) = 2 makes sure that both types of remarks are found.

Andomar
Thanks Andomar - just what I was looking for.
DarrenNavitas
LOL - I was just about to come back and say that it wasn't quite working right - however your amendment above now works perfectly!CheersDarren
DarrenNavitas
+2  A: 

WHERE (TravelRecordRmks.RemarkText = 'LOWCOST BOOKING') and (TravelRecordRmks.RemarkText = 'CREDIT CARD USED')

It looks like you need OR instead of AND.

SQL boolean logic (OR and AND) is not interpreted the same way as everyday English.

JasDev
Beat me to the punch. I would add - remember that you're only evaluating one row at a time, so when you say something like this:`WHERE (TravelRecordRmks.RemarkText = 'LOWCOST BOOKING') and (TravelRecordRmks.RemarkText = 'CREDIT CARD USED')`You're looking at one value, and seeing if it equals two different thigns at the same time. An OR would fix this.
steve
-1 This would also match `BDF232` because it has a low cost booking
Andomar
Surely this would bring back all records as each one of the TravelEventIDs in TravelRecordRmks contains either LOWCOST BOOKING or CREDIT CARD USED.Therefore I would get all the Record locators returned - not what I'm looking for.I need only the record locators that contain BOTH the remarks
DarrenNavitas
A: 

Try something like this:

SELECT DISTINCT
  tr.RecordLocator
FROM
  TravelRecords tr
  INNER JOIN TravelRecordRmks rmk
    ON tr.TravelEventID = rmk.TravelEventID
WHERE
  rmk.RemarkText = 'LOWCOST BOOKING'
  OR rmk.RemarkText = 'CREDIT CARD USED'
ORDER BY
  tr.RecordLocator
TLiebe
-1 This would also match BDF232 because it has a low cost booking
Andomar
Good point. Your answer above looks like it will do what was needed.
TLiebe
A: 

The problem is with your AND condition. You are asking for records from TravelRecordRmks where it is true that the RemarkText field is 'LOWCOST BOOKING' and simultaneously is 'CREDIT CARD USED'.

What you want is:

   WHERE RemarkText IN ('LOWCOST BOOKING', 'CREDIT CARD USED')

which will find lines containing either value.

Larry Lustig
-1 This would also match BDF232 because it has a low cost booking
Andomar
My bad. Reading too fast.
Larry Lustig
+1  A: 

You can't use an and like that to check for values that occur on multiple rows. The best you can do is use an or and count to make sure you match the number of conditions that you want. Something like:

select
  tr.RecordLocator
from
  TravelRecords tr
  join (
    select 
      r.TravelEventID,
      count(*)
    from
      TravelRecords r
      join TravelRecordRmks rr on r.TravelEventID = rr.TravelEventID
    where
      rr.Text = 'LOWCOST BOOKING' or rr.Text = 'CREDIT CARD USED'
    group by
      r.TravelEventID
    having
      count(*) = 2   -- must match both (or have one of them twice) for a single TravelEventID
  ) x on x.TravelEventID = tr.TravelEventID
order by
  tr.RecordLocator
Donnie
+1 Though you probably should group on TravelEventID and RemarkText, or you'd match an event which has multiple 'CREDIT CARD USED' rows
Andomar
+2  A: 

In addition to Andomar's IF EXISTS logic, another way that you could do it would be two joins to the remarks table:

SELECT
     TR.record_locator,
FROM
     Travel_Records TR
INNER JOIN Travel_Record_Remarks TRR1 ON
     TRR1.travel_event_id = TR.travel_event_id AND
     TRR1.remark_text = 'LOWCOST BOOKING'
INNER JOIN Travel_Record_Remarks TRR2 ON
     TRR2.travel_event_id = TR.travel_event_id AND
     TRR2.remark_text = 'CREDIT CARD USED'

There is a flaw with this statement though, which is that if a travel event has two remarks with the same text then you might end up getting back multiple rows with the same record locator.

Tom H.
...which can be easily fixed with a DISTINCT clause.
James