I’m new to SQL and I want to implement the following query:
I’ve got two tables, LicenseTbl and UnlockTbl:
LicenseTbl contains information about a purchased software license:
LicenseID, ProgramID, Owner, Location, OrderNo, BlockTime
UnlockTbl contains information about a specific software registration:
UnlockID, LicenseID (foreign key into LicenseTbl), Timestamp, SerialNo, Key, UninstallTime
where BlockTime and UninstallTime contain a timestamp if the license was blocked or the software uninstalled and NULL otherwise.
I want to devise a query that gives me ALL LicenseIDs for which the following conditions hold:
- belongs to a given customer,
- is not blocked,
- is either not listed in the UnlockTbl or there are < X different SerialNo’s in lines which are not marked as uninstalled.
I have written this, but I’m not sure if it is absolutely correct (it’s one of my first SQL queries ever):
SELECT LicenseID FROM LicenseTbl
JOIN UnlockTbl
ON (LicenseTbl.LicenseID = UnlockTbl.LicenseID)
WHERE LicenseTbl.OrderNo = '$givenOrderNo'
AND LicenseTbl.Owner = '$givenOwner'
AND LicenseTbl.Location = '$givenLocation'
AND LicenseTbl.BlockTime IS NULL
AND UnlockTbl.UninstallTime IS NULL
GROUP BY LicenseTbl.LicenseID, UnlockTbl.Key
HAVING COUNT(*) < $X
(which is supposed to mean, list all licenses which have only been used less than X times simultaneously. I would prefer those that have been used the least first but don’t know how to sort like that.)
This is a good start, but I would change the query to the following…
1).
LEFT JOINA
LEFT JOINensures that all rows inLicenseTblare returned, even if there are no matches in theUnlockTbltable. (If there are no matches, theUnlockTbltable’s values are all represented asNULL.)2).
UnlockTbl.UninstallTime IS NULLin theJOINand not theWHEREThe
WHEREclause is applied after theJOIN. This means that any records inUnlockTblwhereUninstallTimehave a real value (NOT NULL) get joined and then get filtered out. This in turn means that if all the relevant records inUnlockTblhave a non-NULL value inUninstallTime, all the rows for that License will get filtered.3).
GROUP BYon just the license, not the Key.Simply, I don’t know why you had it there, and it doesn’t appear in the English description of what you want.
As you want a list of LicenseIDs, grouping by only that field ensures that you get one record per LicenseID.
4).
HAVINGclause modified to look atCOUNT(DISTINCT SerialNo)COUNT(*)counts all records. Even if there was no match (All theUnlockTblvalues appearing as NULL), this would return1.COUNT(SerialNo)counts only records whereSerialNois NOT NULL. If there was no match (All theUnlockTblvalues appearing as NULL), this would return0.COUNT(DISTINCT SerialNo)also counts only records whereSerialNois NOT NULL, but treats duplicates of the sme value as just 1 entry.5).
ORDER BY COUNT(DISTINCT SerialNo)Takes the same value as is being filtered in the
HAVINGclause, and orders by it.