Как создать запрос к существующей таблице и построить таблицу (представление) с агрегированными данными и ограничением?

У меня есть база данных MS-SQL, которую я использую для хранения данных/информации, поступающих от оборудования, установленного на некоторых транспортных средствах (1-3 устройства на транспортное средство).

На данный момент в базе данных есть таблица DeviceStatus - большая таблица, в которой хранится вся информация с оборудования, когда оно подключается к TCP-серверу. Записи добавляются (sql INSERT) или обновляются (sql UPDATE) здесь.

Таблица выглядит следующим образом:
введите здесь описание изображения

Sample data:   
1040    305 3   8.00    0
1044    305 2   8.00    0
1063    305 1   8.01    1.34
1071    312 2   8.00    0
1075    312 1   8.00    1.33
1078    312 3   8.00    0
1099    414 3   8.00    0
1106    414 2   8.01    0
1113    102 1   8.01    1.34
1126    102 3   8.00    0

Примечание: Консоль водителя всегда связана с устройством, установленным на первой позиции (это расширение устройства на позиции 1; очевидно, что на транспортное средство приходится только одна консоль), поэтому, это будет своего рода ограничение, чтобы иметь правильную информацию в желаемой таблице (представлении), представленной ниже :).

Мне нужен SQL-запрос (команда/инструкция) для создания таблицы (представления) для так называемой «Таблицы версий программного обеспечения», где я могу увидеть версию программного обеспечения для всех устройств, установленных в транспортных средствах (все, что подключалось и общалось с сервером)... что-то вроде таблицы ниже:
введите здесь описание изображения

Примечание: Устройство №1 для 414 отсутствует, потому что оно не обменивалось данными (я думаю, еще нет...)


person סטנלי גרונן    schedule 27.11.2015    source источник
comment
у вас только 3 версии ПО или бесконечное количество ПО? Образец, который мы можем скопировать/вставить, будет лучше, чем изображение. Вы можете использовать SQLFiddle. Почему нет позиции 1 для 414, но я вижу device1SW VERsion?   -  person Julien Vavasseur    schedule 27.11.2015
comment
@JulienVavasseur: Да, максимум 3 на транспортное средство: обычно на каждой двери установлено одно устройство.   -  person סטנלי גרונן    schedule 27.11.2015
comment
Вместо 2-х случайных скриншотов вы должны включить выборку из 5-10 DeviceInfo и получившуюся SwVersion. Afaik, более половины информации, отображаемой в SwVersions, не отображается в таблице DeviceInfo.   -  person Julien Blanchard    schedule 27.11.2015
comment
о, nvm, я только что понял, откуда берется ваша версия DriverConsoleVersion, я думал, вы группируете по Vehicule+Console. Кстати, ошибся Жюльен ;)   -  person Julien Blanchard    schedule 27.11.2015
comment
@groenhen Значит, в DeviceInfo никогда не будет более 3 строк для любого данного VehicleNo?   -  person Julien Blanchard    schedule 27.11.2015
comment
@JulienBlanchard: Абсолютно. Как я уже упоминал: максимум 3 (trois :)   -  person סטנלי גרונן    schedule 27.11.2015
comment
@groenhen, вы сказали, что максимум 3 программного обеспечения (и, следовательно, позиции равны 1,2,3), но вы не упомянули, что не может быть повторяющихся строк. Существует огромная разница с проблемой, если могут быть дубликаты.   -  person Julien Blanchard    schedule 27.11.2015
comment
@JulienBlanchard: о, но я сделал: проверьте выше первичный ключ (DeviceSerial). Более того, я упоминал, что записи добавляются (обязательны для новых устройств) или ОБНОВЛЯЮТСЯ :)   -  person סטנלי גרונן    schedule 27.11.2015


Ответы (2)


Мне нравится ответ PIVOT, но вот еще один способ:

select VehicleNo,
max(DriverConsoleVersion) DriverConsoleVersion,
max(case when DevicePosition = 1 then DeviceSwVersion end) Device1SwVersion,
max(case when DevicePosition = 2 then DeviceSwVersion end) Device2SwVersion,
max(case when DevicePosition = 3 then DeviceSwVersion end) Device3SwVersion
from @DeviceInfo
group by VehicleNo
order by VehicleNo

Вы также можете сделать кастинг или форматирование на них. Таким образом, может быть:

       select ...,
       isnull(cast(cast(
          max(case when DevicePosition = 1 then DeviceSwVersion end)
            as decimal(8,2)) / 100) as varchar(5)), '')  Device1SwVersion,
person JBrooks    schedule 28.11.2015
comment
Я запустил его в SQL Fiddle (сейчас я дома), и он работает. Довольно мило, чисто, просто :) Выводит 0 (ноль) для DriverConsoleVersion (объявленного int или smallint в базе данных) для автомобиля 414, возможно, лучше иметь там null, но я полагаю, что это не критично. Да, я тоже поддержу этот ответ. Спасибо, JB, что поделился этим :) - person סטנלי גרונן; 28.11.2015
comment
В документации сказано: CAST ( выражение AS data_type [ ( length ) ] ) , поэтому я предполагаю, что NUMERIC (8, 2) означает, что целевая общая длина будет равна 8, из которых 2 будут использоваться для десятичных знаков ...? Десятичная точка не считается, я полагаю... - person סטנלי גרונן; 28.11.2015
comment
если по какой-либо причине существует версия драйвера с позицией 2 или 3, может быть полезно добавить случай, когда DevicePosition = 1... в max() drivermonsoleversion - person Julien Vavasseur; 28.11.2015
comment
Кажется, он работает нормально даже без order by VehicleNo (последняя строка). :) - person סטנלי גרונן; 30.11.2015
comment
@groenhen это работает без ORDER BY, но ваш заказ не обязательно будет каким-либо заказом. - person JBrooks; 01.12.2015

С информацией, которая у нас есть на данный момент, я думаю, вам нужен запрос с PIVOT:

SELECT P.VehicleNo, V.DriverConsoleVersion, P.[1] AS [Device1SwVersion], P.[2] AS [Device1SwVersion], P.[3] AS [Device1SwVersion]
FROM (
    SELECT VehicleNo, [1], [2], [3]
    FROM (
        SELECT VehicleNo, DevicePosition, DeviceSwVersion
        FROM @DeviceInfo
    ) as d
    PIVOT (
        MAX(DeviceSwVersion)
        FOR DevicePosition IN ([1], [2], [3])
    ) PIV
) P 
LEFT JOIN @DeviceInfo V
    ON V.VehicleNo = P.VehicleNo AND V.DevicePosition = 1;

Вы можете создать представление с таким запросом.

Первый подзапрос получает 4 столбца для устройств от 1 до 3 для каждого транспортного средства. Затем он LEFT JOIN с таблицей SwVersion, чтобы получить версию консоли, связанную с устройством 1.

Вывод:

VehicleNo   DriverConsoleVersion    Device1SwVersion    Device1SwVersion    Device1SwVersion
102         1.34                    8.01                NULL                8.00
305         1.34                    8.01                8.00                8.00
312         1.33                    8.00                8.00                8.00
414         NULL                    NULL                8.01                8.00

Ваши данные:

Declare @DeviceInfo TABLE([DeviceSerial] int, [VehicleNo] int, [DevicePosition] int, [DeviceSwVersion] varchar(10), [DriverConsoleVersion] varchar(10));

INSERT INTO @DeviceInfo([DeviceSerial], [VehicleNo], [DevicePosition], [DeviceSwVersion], [DriverConsoleVersion])
VALUES
    (1040, 305, 3, '8.00', '0'),
    (1044, 305, 2, '8.00', '0'),
    (1063, 305, 1, '8.01', '1.34'),
    (1071, 312, 2, '8.00', '0'),
    (1075, 312, 1, '8.00', '1.33'),
    (1078, 312, 3, '8.00', '0'),
    (1099, 414, 3, '8.00', '0'),
    (1106, 414, 2, '8.01', '0'),
    (1113, 102, 1, '8.01', '1.34'),
    (1126, 102, 3, '8.00', '0')
;
person Julien Vavasseur    schedule 27.11.2015
comment
Хммм, похоже, что-то не так... На некоторых устройствах установлена ​​версия ПО 8.01, их нет в выводе... Может, "8.0x" подойдет? - person סטנלי גרונן; 27.11.2015
comment
просто проблема с типом данных в моей примерной таблице. Я меняю его на varchar. сначала было int, округленное до 8. - person Julien Vavasseur; 27.11.2015
comment
Что, если база данных хранит версию консоли драйвера как целое число (т. е. 134 -> это действительно так...), есть ли какой-то трюк, чтобы преобразовать ее в выходной вид как 1.34, который более удобочитаем для пользователя? С другой стороны, DeviceSwVersion, которую я вижу, - это varchar(). - person סטנלי גרונן; 27.11.2015
comment
Так как 134/100 = 1,34? вы можете использовать: Cast(X as numeric(8,2))/100 - person Julien Vavasseur; 27.11.2015