СОЕДИНЕНИЕ СЛЕВА с оператором И в сочетании с оператором IN дает ошибку: выражение не булева типа, указанное в контексте, в котором ожидается условие

#sql-server #tsql

Вопрос:

Я пытаюсь перенести запрос MySQL на SQL Server. Запрос отлично работает в MySQL, но в SQL Server он завершается ошибкой со следующей ошибкой:

Выражение не булева типа, заданное в контексте, в котором ожидается условие

Я подозреваю, что это связано с тем, что SQL Server не нравится тот факт, что я использую AND оператор в сочетании с IN оператором в условном соединении слева.

запрос

 SELECT *
FROM
  (SELECT dol.Id AS 'log_id',
          c.Id AS 'computer_id',
          COALESCE(r.processor_test, 'Skipped') as 'processor_test',
  COALESCE(r.display_test, 'Skipped') as 'display_test',
  COALESCE(r.storage_test, 'Skipped') as 'storage_test',
  COALESCE(r.network_test, 'Skipped') as 'network_test',
  COALESCE(r.keyboard_test, 'Skipped') as 'keyboard_test',
  COALESCE(r.mouse_test, 'Skipped') as 'mouse_test',
          COALESCE(user_fields.customer_id, 'None') AS 'customer_id',
          COALESCE(user_fields.order_no, 'None') AS 'order_no',
          COALESCE(user_fields.technician_id, 'None') AS 'technician_id',
          CONCAT('Customer ID: ', customer_id, '<br>Order No: ', order_no, '<br>Technician ID: ', technician_id) AS 'userfields',
          hd.Id AS 'hard_disk_id',
          dol.StartTime AS 'start_time',
          hd.Serial AS 'hard_disk_serial',
          CONCAT(hd.Gigabytes, ' GB') AS 'gigabytes',
          CONCAT('<b>Vendor: </b>', hd.Vendor, '<br><br><b>Model: </b>', hd.Product, '<br><br><b>Serial: </b>', hd.Serial, '<br><br><b>Size: </b>', hd.Gigabytes, ' GB<br><br><b>Pre-wipe SMART health status: ', dol.SmartOverallPre, '<br><br><b>Post-wipe SMART health status: ', dol.SmartOverallPost) AS 'target_drive_detailed',
          CONCAT('Vendor: ', hd.Vendor, '<br><br>Product: ', hd.Product, '<br><br>Serial: ', hd.Serial, '<br><br>Drive Media Type: ', hd.DriveMediaType , '<br><br>Size: ', hd.Gigabytes, ' GB<br><br><b>Interface: ', hd.Interface) AS 'drive_detailed',
          CONCAT(hd.Serial, ' (',hd.Gigabytes, ' GB)') AS 'target_drive',
          CASE
              WHEN dol.PatternName = 'NIST 800-88r1 (recommended)' THEN 'NIST 800-88r1'
              ELSE dol.PatternName
          END AS 'pattern_name',
          CASE
              WHEN dol.Success = '1' THEN 'SUCCESS'
              WHEN dol.Success = '0' THEN 'FAILED'
          END AS 'wipe_status',
          dol.Errors AS 'errors',
          dol.DirtySectors AS 'dirty_sectors',
          dol.Tool AS 'tool',
          dol.Kernel AS 'kernel',
          dol.JobUUID AS 'job_uuid',
          dol.UUID AS 'uuid',
          CONVERT(VARCHAR(20), dol.EndTime, 120) AS 'end_time',
          substring(CONVERT(VARCHAR(20), dol.EndTime, 120), 1, 11) AS 'end_time_short',
          dol.NumPasses AS 'num_passes',
          dol.TrimPasses AS 'trim_passes',
          dol.SectorsOverwritten AS 'sectors_overwritten',
          dol.SectorsNotOverWritten AS 'sectors_not_overwritten',
          dol.SectorsVerified AS 'sectors_verified',
          dol.CustomField AS 'custom_field_legacy',
          CASE
              WHEN dol.NISTMethodTypeId = '1' THEN 'Unknown'
              WHEN dol.NISTMethodTypeId = '2' THEN 'Clear'
              WHEN dol.NISTMethodTypeId = '3' THEN 'Purge'
          END AS 'nis_method_type',
          CASE 
              WHEN dol.DCOFound = '1' AND dol.DcoRemoved = '1' THEN CONCAT('Yes /</td><td> Yes', ' ') 
              WHEN dol.DCOFound = '0' AND dol.DcoRemoved = '0' THEN CONCAT('No /</td><td> No', ' ') 
              WHEN dol.DCOFound = '1' AND dol.DcoRemoved = '0' THEN CONCAT('Yes /</td><td> No', ' ') 
              WHEN dol.DCOFound = '0' AND dol.DcoRemoved = '1' THEN CONCAT('No /</td><td> Yes', ' ') 
          END AS 'dco_foundremoved',
          CASE
              WHEN dol.DCOLocked = '1' THEN 'Yes'
              WHEN dol.DCOLocked = '0' THEN 'No'
          END AS 'dco_locked',
          CASE 
              WHEN dol.HPAFound = '1' AND dol.HPARemoved = '1' THEN CONCAT('Yes /</td><td> Yes', ' ') 
              WHEN dol.HPAFound = '0' AND dol.HPARemoved = '0' THEN CONCAT('No /</td><td> No', ' ') 
              WHEN dol.HPAFound = '1' AND dol.HPARemoved = '0' THEN CONCAT('Yes /</td><td> No', ' ') 
              WHEN dol.HPAFound = '0' AND dol.HPARemoved = '1' THEN CONCAT('No /</td><td> Yes', ' ') 
          END AS 'hpa_foundremoved',
          CASE 
              WHEN dol.AMAXFound = '1' AND dol.AMAXRemoved = '1' THEN CONCAT('Yes /</td><td> Yes', ' ') 
              WHEN dol.AMAXFound = '0' AND dol.AMAXRemoved = '0' THEN CONCAT('No /</td><td> No', ' ') 
              WHEN dol.AMAXFound = '1' AND dol.AMAXRemoved = '0' THEN CONCAT('Yes /</td><td> No', ' ') 
              WHEN dol.AMAXFound = '0' AND dol.AMAXRemoved = '1' THEN CONCAT('No /</td><td> Yes', ' ') 
          END AS 'amax_foundremoved',
          c.Vendor AS 'vendor',
          c.Model AS 'computer_model',
          c.ComputerSerial AS 'computer_serial',
          CONCAT('<b>Vendor: </b>', c.Vendor, '<br><br><b>Model: </b>', c.Model, '<br><br><b>Serial: </b>', c.ComputerSerial) as 'computer_summary',
          CONCAT('<b>Vendor: </b>', c.MotherboardVendor, '<br><br><b>Model: </b>', c.MotherboardModel) as 'motherboard_summary',
          CONCAT(first_cpu.Name, ' ', first_cpu.Speed) as 'cpu_summary',
          CONCAT(first_nic.Vendor, '<br><br>', first_nic.Product) as 'nic_summary',
          CONCAT(first_vc.Vendor, '<br><br>', first_vc.Product) as display_summary,
          CONCAT('<b>USB1 Ports: </b>', c.UsbPorts, '<br><br><b>USB2 Ports: </b>', c.Usb2Ports, '<br><br><b>USB3 Ports: </b>', c.Usb3Ports) as usb_ports,
          c.Memory AS 'computer_memory',
          hd.DriveMediaType AS 'hard_disk_drive_media_type',
          hd.Product AS 'hard_disk_product',
          hd.Vendor AS 'hard_disk_vendor'
   FROM DiskOperationLog dol
   LEFT JOIN
     (SELECT juf.JobUUID AS 'juf_job_uuid',
             COALESCE(max(CASE
                              WHEN juf.FieldName = 'Customer ID' THEN juf.FieldValue
                          END), 'None') AS customer_id,
             COALESCE(max(CASE
                              WHEN juf.FieldName = 'Order No' THEN juf.FieldValue
                          END), 'None') AS order_no,
             COALESCE(max(CASE
                              WHEN juf.FieldName = 'Technician ID' THEN juf.FieldValue
                          END), 'None') AS technician_id
      FROM DiskOperationLog dol
      INNER JOIN JobUserFields juf ON dol.JobUUID = juf.JobUUID
      GROUP BY juf.JobUUID) user_fields ON dol.JobUUID = user_fields.juf_job_uuid
   LEFT JOIN HardDisk hd ON hd.Id = dol.HardDiskId and (hd.Id, dol.EndTime) IN (select max(hd.Id) as Id, max(dol.EndTime) as EndTime from DiskOperationLog dol join HardDisk hd on hd.Id = dol.HardDiskId group by hd.Id)
   LEFT JOIN Computer c ON c.Id = hd.ComputerId
   LEFT JOIN
(select t.computer_id, 
    MAX(
        CASE 
            WHEN Type='Processor' 
            THEN Result
            ELSE NULL
        END
    ) AS 'processor_test',
    MAX(
        CASE 
            WHEN Type='Display' 
            THEN Result
            ELSE NULL
        END
    ) AS 'display_test',
    MAX(
        CASE
            WHEN Type='Network' 
            THEN Result
            ELSE NULL
        END
    ) AS 'network_test',
    MAX(
        CASE 
            WHEN Type='Keyboard'
            THEN Result
            ELSE NULL
        END
    ) AS 'keyboard_test',
    MAX(
        CASE 
            WHEN Type='Mouse'
            THEN Result
            ELSE NULL
        END
    ) AS 'mouse_test',
    MAX(
        CASE 
            WHEN Type='Storage'
            THEN Result
            ELSE NULL
        END
    ) AS 'storage_test'
  from (    
    select c.Id as computer_id,
        hst.Type, 
        case min(
            case hst.Result 
              when'Fail' then 1
              when'Skipped' then 2
              when'Pass' then 3
            end) 
          when 1 then 'Fail' 
          when 2 then 'Skipped'     
          when 3 then 'Pass'
        end Result
    from Computer c
    inner join HardwareTest ht on c.Id = ht.ComputerId
    inner join HardwareSubTest hst on ht.Id = hst.HardwareTestId
    group by c.Id, hst.Type
    ) t
 group by computer_id) r on c.Id = r.computer_id
   JOIN
     (SELECT *
      FROM Cpu cpu
      WHERE Id in
          (SELECT min(Id)
           FROM Cpu
           GROUP BY ComputerId)) AS first_cpu ON c.Id = first_cpu.ComputerId
   JOIN
     (SELECT *
      FROM Nic nic
      WHERE Id in
          (SELECT min(Id)
           FROM Nic
           GROUP BY ComputerId)) AS first_nic ON c.Id = first_nic.ComputerId
   JOIN
     (SELECT *
      FROM VideoCard vc
      WHERE Id in
          (SELECT min(Id)
           FROM VideoCard
           GROUP BY ComputerId)) AS first_vc ON c.Id = first_vc.ComputerId) AS query_result
 

Я не уверен, почему, однако, использование AND in a LEFT JOIN поддерживается в SQL Server. Что я упускаю?

скриншот

Комментарии:

1. Извините, сегодня утром у нас закончился кофе 😀 добавил запрос.

2. Проблема в том, что вы пытаетесь сопоставить несколько столбцов.

3. Я вижу, нормально ли, что я все еще получаю ошибку , если пытаюсь сопоставить один столбец? Я просто проверял, сработает ли это при сопоставлении одного столбца.

4. На самом деле я тоже не думаю, что это сработает, потому что это не скалярный запрос. Я на своем телефоне, иначе я бы набрал его.

5. Не спеши, спасибо

Ответ №1:

Использование И в соединениях поддерживается в SQL Server, но вы не можете сопоставлять несколько столбцов. SQL server разрешает только один столбец.

Попробуйте изменить свое соединение с помощью этого:

    LEFT JOIN HardDisk hd 
   ON hd.Id = dol.HardDiskId 
   and hd.Id in (select max(hd.Id) from DiskOperationLog dol join HardDisk hd on hd.Id = dol.HardDiskId group by hd.Id)
   AND dol.EndTime in (select max(dol.EndTime) from DiskOperationLog dol join HardDisk hd on hd.Id = dol.HardDiskId group by hd.Id)
 

Комментарии:

1. Спасибо @RXolio, похоже, это не работает .

2. Я изменил свой ответ, когда подзапрос возвращает более 1 значения, которые вы можете использовать В

3. Это, кажется, работает, спасибо! Я проверю результат как можно скорее.

4. Было APPLY бы лучше, так как вам не нужно повторять

5. В лучшем случае вся эта логика объединения полностью избыточна. Хуже того, эта логика конечного времени может даже исключать столбцы HD из результатов. Не видя данных, я не могу этого определить.

Ответ №2:

Как упоминалось в другом ответе, SQL Server не поддерживает компараторы значений строк.

Но чтобы избежать повторения кода, вы можете использовать APPLY

    OUTER APPLY (
      select
        MaxId = max(hd.Id),
        MaxEndTime = max(dol.EndTime)
      from DiskOperationLog dol2
      join HardDisk hd on hd.Id = dol2.HardDiskId
      group by hd.Id
   ) dol2
   LEFT JOIN HardDisk hd 
   ON hd.Id = dol.HardDiskId 
   and hd.Id = dol2.MaxId
   AND dol.EndTime = dol2.MaxEndTime
 

Я должен сказать, что весь этот запрос выглядит так, как будто он мог бы работать с хорошей дозой оконных функций, особенно в этом разделе выше.

Ответ №3:

Прочитав очень внимательно, я уверен, что эта часть вашего запроса абсолютно ничего не дает. Я не уверен, какова была предполагаемая цель в попытке соответствовать dol.EndTime , но это неправильный способ сделать это.

 LEFT JOIN HardDisk hd ON hd.Id = dol.HardDiskId and (hd.Id, dol.EndTime) IN (
   select max(hd.Id) as Id, max(dol.EndTime) as EndTime
   from DiskOperationLog dol join HardDisk hd on hd.Id = dol.HardDiskId group by hd.Id
)
 

Я бы посоветовал вам перейти на приведенное ниже или изучить, какова цель, и обновить свой вопрос:

 LEFT JOIN HardDisk hd ON hd.Id = dol.HardDiskId