| Author |
Topic |
|
dazza5621
Starting Member
7 Posts |
Posted - 2009-04-27 : 08:29:45
|
| Hi All, I have two tables computers and software and need to select a computer that has either app1, app2 or app3 but not app4, I have the first bit but can't seem to get the exclusion, this is what I have so far:SELECT computers.guid, computers.name, software.nameFROM softwareINNER JOIN computers ON software.guid = computers.guidWHERE software.name IN ('app1','app2','app3')Thanks |
|
|
whitefang
Enterprise-Level Plonker Who's Not Wrong
272 Posts |
Posted - 2009-04-27 : 08:35:06
|
| You have bad table design, look into normalization. You need a junction table connecting computers and software together.Your query should work? It excludes app4? |
 |
|
|
dazza5621
Starting Member
7 Posts |
Posted - 2009-04-27 : 08:40:39
|
| Unfortunatley, it is not my design so I have to work with it, would I be best creating a temp table and working with that?Thanks |
 |
|
|
whitefang
Enterprise-Level Plonker Who's Not Wrong
272 Posts |
Posted - 2009-04-27 : 08:42:05
|
| SELECT computers.guid, computers.name, software.nameFROM softwareINNER JOIN computers ON software.guid = computers.guidWHERE software.name IN ('app1','app2','app3')Your query should ALREADY exclude app4 (due to the WHERE software.name IN ('app1','app2','app3') clause). |
 |
|
|
dazza5621
Starting Member
7 Posts |
Posted - 2009-04-27 : 08:42:05
|
| Maybe I didn't explain correctly I want a computer if it has app1, app2 or app3 but not if it has app4 |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2009-04-27 : 08:43:56
|
[code]SELECT computers.guid, computers.name, software.nameFROM softwareINNER JOIN computers ON software.guid = computers.guidWHERE software.name IN ('app1','app2','app3')SELECT c.GUID, c.Name, s.NameFROM Computers AS cINNER JOIN Software AS s ON s.GUID = c.GUIDINNER JOIN ( SELECT GUID FROM Software GROUP BY GUID HAVING MAX(CASE WHEN Name IN ('app1', 'app2', 'app3') THEN 1 ELSE 0 END) = 1 AND MAX(CASE WHEN Name = 'app4') THEN 1 ELSE 0 END) = 0 ) AS d ON d.GUID = s.GUID[/code] E 12°55'05.63"N 56°04'39.26" |
 |
|
|
whitefang
Enterprise-Level Plonker Who's Not Wrong
272 Posts |
Posted - 2009-04-27 : 08:46:27
|
| SELECT computers.guid, computers.name, software.nameFROM softwareINNER JOIN computers ON software.guid = computers.guidWHERE computers.guid NOT IN (SELECT software.guid FROM software WHERE software.guid = computers.guid AND software.name = 'app4'); |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2009-04-27 : 08:54:04
|
That will exclude App4.Where are App1 or App2 or App3 included? E 12°55'05.63"N 56°04'39.26" |
 |
|
|
whitefang
Enterprise-Level Plonker Who's Not Wrong
272 Posts |
Posted - 2009-04-27 : 08:56:32
|
WHERE computers.guid NOT INThis will exclude all computers that contain App4. quote: Originally posted by dazza5621 Maybe I didn't explain correctly I want a computer if it has app1, app2 or app3 but not if it has app4
EDIT: I was assuming there was only 4 app kinds.SELECT computers.guid, computers.name, software.nameFROM softwareINNER JOIN computers ON software.guid = computers.guidWHERE computers.guid NOT IN (SELECT software.guid FROM software WHERE software.guid = computers.guid AND software.name = 'app4') AND software.name IN ('app1','app2','app3'); |
 |
|
|
dazza5621
Starting Member
7 Posts |
Posted - 2009-04-27 : 09:07:34
|
| Thanks Whitefang the last post did the trick |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2009-04-27 : 09:10:54
|
[code]DECLARE @Software TABLE ( [guid] INT, Name VARCHAR(10) )INSERT @SoftwareSELECT 1, 'app1' UNION ALLSELECT 2, 'app1' UNION ALLSELECT 2, 'app3' UNION ALLSELECT 3, 'app1' UNION ALLSELECT 3, 'app3' UNION ALLSELECT 3, 'app2' UNION ALLSELECT 3, 'app4' UNION ALLSELECT 1, 'app4'DECLARE @Computers TABLE ( [guid] INT, Name VARCHAR(10) )INSERT @ComputersSELECT 1, 'Comp1' UNION ALLSELECT 2, 'Comp2' UNION ALLSELECT 3, 'Comp3' UNION ALLSELECT 4, 'Comp4'-- Peso (9 reads)SELECT c.GUID, c.Name, s.NameFROM @Computers AS cINNER JOIN @Software AS s ON s.GUID = c.GUIDINNER JOIN ( SELECT GUID FROM @Software GROUP BY GUID HAVING MAX(CASE WHEN Name IN ('app1', 'app2', 'app3') THEN 1 ELSE 0 END) = 1 AND MAX(CASE WHEN Name = 'app4' THEN 1 ELSE 0 END) = 0 ) AS d ON d.GUID = s.GUID-- WhiteFang (24 reads)SELECT c.guid, c.name, s.nameFROM @software AS sINNER JOIN @computers AS c ON s.guid = c.guidWHERE c.guid NOT IN (SELECT x.guid FROM @software as x WHERE x.guid = c.guid AND x.name = 'app4') AND s.name IN ('app1', 'app2', 'app3')[/code] E 12°55'05.63"N 56°04'39.26" |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2009-04-27 : 09:11:39
|
dazza5621, why would you want to use three times as much reads from the database than the suggestion I made? E 12°55'05.63"N 56°04'39.26" |
 |
|
|
whitefang
Enterprise-Level Plonker Who's Not Wrong
272 Posts |
Posted - 2009-04-27 : 09:14:54
|
| Funny when the Execution Plan shows that your query takes 74% more time and resources to complete and mine takes only 26%.dazza5621, why would you want to use 74% more time and resources from the database than the suggestion I made? |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2009-04-27 : 09:21:32
|
75% (Peso part in batch) vs 25% (WhiteFang in batch) is 200% more.Learn your percentage calculations.And you're wrong about the resources. SQL Profiler says I am using only 9 reads from the database and you are using 24 reads.Reads are the expensive operation in a query.Populate the sample data with 5000 records and try again.I did and got half the DURATION as WhiteFang, and a third of the reads.Still Batch percentage in SSMS said 65% for my suggestion and 35% for WhiteFang. E 12°55'05.63"N 56°04'39.26" |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2009-04-27 : 09:50:28
|
And you can try this, which has more reads than WhiteFang but a quarter the DURATIONSELECT GUID, ComputerName, SoftwareNameFROM ( SELECT c.GUID, c.Name AS ComputerName, s.Name AS SoftwareName, MAX(CASE WHEN s.Name = 'App4' THEN 1 ELSE 0 END) OVER (PARTITION BY s.GUID) AS recID FROM #Computers AS c INNER JOIN #Software AS s ON s.GUID = c.GUID WHERE s.Name IN ('app1', 'app2', 'app3', 'app4') ) AS dWHERE recID = 0So I guess which method you should use is dependant on how many records you have in your table, and which indexes are present. E 12°55'05.63"N 56°04'39.26" |
 |
|
|
SwePeso
Patron Saint of Lost Yaks
30421 Posts |
Posted - 2009-04-27 : 09:55:13
|
WhiteFang, you also are too reliant on the batch percentage.See this example;WITH Yak (i)AS ( SELECT 0 UNION ALL SELECT i + 1 FROM Yak WHERE i < 10)SELECT *FROM YakSELECT 0 AS i UNION ALLSELECT 1 AS i UNION ALLSELECT 2 AS i UNION ALLSELECT 3 AS i UNION ALLSELECT 4 AS i UNION ALLSELECT 5 AS i UNION ALLSELECT 6 AS i UNION ALLSELECT 7 AS i UNION ALLSELECT 8 AS i UNION ALLSELECT 9 AS i UNION ALLSELECT 10 AS i UNION ALL First query has 103 reads and 46 ms duration but only 36% of the batch.Second query has 0 reads and 6 ms but also 64% of batch.How do you correlate that percentage? E 12°55'05.63"N 56°04'39.26" |
 |
|
|
|