Use multiple counts in SQL Server 2005 - sql-server-2005

select p.intprojectid, p.vcprojectname, md.intmoduleid,
md.vcmodulename, md.intscreensfunc, md.vcname
from projects as p
left join (select m.intprojectid, m.intmoduleid, m.vcmodulename,
s.intscreensfunc, s.vcname
from modules as m
left join screens_func as s on m.intmoduleid = s.intmoduleid) md
on p.intprojectid = md.intprojectid
This query will return:
no |project-name|mod-id|mod-name | screen-id | screen-name
----------------------------------------------------------------
2 Project-1 4 mod-1 11 scr1
2 Project-1 4 mod-1 12 scr2
2 Project-1 4 mod-1 13 scr3
2 Project-1 4 mod-1 14 scr4
2 Project-1 8 Module-2 NULL NULL
Now I want to count no.of mod-name and no.of.screen-name in project-1. i.e. I want the query to return
project-name no.of.mod no.of.screen
------------------------------------------------
Project-1 2 4

It's definitely possible to return multiple counts.
In other words, your query could be modified as follows:
select p.vcprojectname, COUNT(DISTINCT md.intmoduleid) as no.of.mod, COUNT(md.intscreensfunc) as no.of.screen
from projects as p
left join (select m.intprojectid, m.intmoduleid, m.vcmodulename, s.intscreensfunc, s.vcname
from modules as m
left join screens_func as s
on m.intmoduleid=s.intmoduleid)md
on p.intprojectid=md.intprojectid
GROUP BY p.vcprojectname
Based on your example data, I inferred that there would be a one-many relationship between modules and screens and thus you would want a distinct count for modules but that the same requirement would not be needed for screens (since it appears that one screen would not appear multiple times in a given module) If that is not the case, you can also add distinct to the count of screens.

Related

MS access join two tables, get unique rows

This is a modification to a previous question original answer , I hope the proper thing to do is start a new thread.
I have a table called Parts, PartRefID is the PK
PartRefID PartDefID AssemblyID
1 2 c63df10b-8250-4aa5-9889-9e8046331dbf
11 1 db51f4a8-3ffa-41f7-81c1-a9accbbb299a
67 6 136fc5d8-7b65-41b5-bca3-7d4180a1e0ab
77 5 38fa8b7a-2945-4546-8eab-7865a1e515b2
133 2 c63df10b-8250-4aa5-9889-9e8046331dbf
134 6 136fc5d8-7b65-41b5-bca3-7d4180a1e0ab
I need to extract rows with a unique AssemblyID. This was answered by GMB with the following sql:
select *
from parts as p
where [PartRefID] = (
select max(p1.[PartRefID])
from parts as p1
where p1.[AssemblyID] = p.[AssemblyID] and p1.[PartDefID] = 2
)
which worked beautifully. However requirements have changed and I must ignore the PartDefID field and there could also be AssemblyID's which represent parts I do not want.
The AssemblyID's shown in the above table represent an electrical connector part.
Electrical connector parts will ALWAYS have a Partclass of 1 which is defined in another table called PartDefinitions shown here:
PartDefID PartClass PartNumber
1 1 MS27467T23F55P
2 1 330-00186-09
3 2 336-00024-00
4 2 336-00022-00
5 1 MS27468T23F55S
6 1 330-00184-09
with my limited sql knowledge I decided a join was necessary and came up with the following code:
SELECT Parts.*, PartDefinitions.PartClass
From PartDefinitions
INNER Join Parts
On PartDefinitions.PartDefID = Parts.PartDefID
Where (((PartDefinitions.PartClass) = 1))
this gets me close, it produces all the parts in the parts table which are connectors. However there are some duplicate AssemblyID's.
what I need is to produce the following:
PartRefID PartDefID AssemblyID
1 2 c63df10b-8250-4aa5-9889-9e8046331dbf
11 1 db51f4a8-3ffa-41f7-81c1-a9accbbb299a
67 6 136fc5d8-7b65-41b5-bca3-7d4180a1e0ab
77 5 38fa8b7a-2945-4546-8eab-7865a1e515b2
my apologies if I have not made a clear and concise question
thanks for any help
and thanks again GMB
If I understand correctly, you want to filter by the PartClassId in both the subquery and the outer query:
select p.*, pd.PartClass
From Parts as p inner join
PartDefinitions as pd
on pd.PartDefID = p.PartDefID
where pd.PartClassId = 1 and
p.pPartRefID = (select max(p2.pPartRefID)
from parts as p2 inner join
PartDefinitions as pd2
on pd2.PartDefID = pd.PartDefID
where p2.AssemblyID = p.AssemblyID and
p2.PartClassId = 1
)

Update table by all records from second table

Morning,
I have two tables. The first table (SecurityRules) is a list of security rules:
ID srRight srRole
1 4 NULL
2 2 32
The second table (Projects) is a list of Projects :
ProjId prRight prRole
1 0 NULL
2 0 32
3 0 NULL
I need to update the list of projects with all the records from SecurityRules and update the prRight column based on the Role from both tables. The Right values are bitwise-organised.
I used the following SQL update query to do this:
Update Projects
-- Perform binary sum
Set prRight = prRight | srRight
From SecurityRules
Where (srRole is Null) --Always apply srRight if srRole is not defined
OR (srRole is Not Null And srRole=prRole) --Apply right if both roles are equal
The expected result is:
ProjId prRight prRole
1 4 NULL
2 6 32
3 4 NULL
But I get:
ProjId prRight prRole
1 4 NULL
2 4 32
3 4 NULL
It looks like the update is done only by the first record of the SecurityRules table. And I need to apply all the records from the SecurityRules table to all records of the Project table.
If I create a simple loop and manually looped all the records from SecurityRules it is working fine, but the performance is very poor if you have to compare 10 security rules to 2000 projects...
Any suggestion?
Arno
This answer is based on the code in this answer for generating a bitwise OR of values. It uses CTEs to generate a bit mask for each rights value and then the overall bitwise OR by summing the distinct bit masks present in each of the rights values. The output of the last CTE is then used to update the Projects table:
WITH Bits AS (
SELECT 1 AS BitMask
UNION ALL
SELECT 2 * BitMask FROM Bits
WHERE BitMask < 65536
),
NewRights AS (
SELECT ProjId, SUM(DISTINCT BitMask) AS NewRight
FROM Projects p
JOIN SecurityRules s ON s.srRole IS NULL OR s.srRole = p.prRole
JOIN Bits b ON b.BitMask & s.srRight > 0
GROUP BY ProjID
)
UPDATE p
SET p.prRight = n.NewRight
FROM Projects p
JOIN NewRights n ON n.ProjId = p.ProjId
Resultant Projects table:
ProjId prRight prRole
1 4 null
2 6 32
3 4 null
Demo on dbfiddle
If I understand correctly, you have a direct match on the srRole column and then a default rule that applies to everyone.
The simplest method (in this case) is to use joins in the update:
update p
Set prRight = p.prRight | srn.srRight | coalesce(sr.srRight, 0)
From Projects p join
SecurityRules srn
on srRole is null left join
SecurityRules sr
on sr.srRole = p.prRole;
Here is a db<>fiddle.
You might be safer assuming no default rule. And that prRight could be NULL:
update p
Set prRight = coalesce(p.prRight, 0) | coalesce(srn.srRight, 0) | coalesce(sr.srRight, 0)
From Projects p left join
SecurityRules srn
on srRole is null left join
SecurityRules sr
on sr.srRole = p.prRole;
That said, I would recommend that you consider revising your data model. Bit fiddling is a lot of fun in programming languages. However, it is generally not the best approach in databases. Instead use junction tables, unless your application has some real need for bit switches.

Find 'Most Similar' Items in Table by Foreign Key

I have a child table with a number of charact/value pairs for a given 'material' (MaterialID). Any material can have a number of charact values and may have several of the same name (see id's 2,3).
The table has a large number of records (8+ million). What I'm trying to do is find the materials that are the most similar to a supplied material. That is, when I supply a MaterialID, I would like an ordered list of the most similar other materials (those with the most matching charact/value pairs).
I've done some research but, I may be missing some key terms or just not conceptualizing the problem correctly.
Any hints as to how to go about this would be very much appreciated.
ID MaterialID Charact Value
1 1 ROT_DIR CCW
2 1 SPECIAL_FEATURE CATALOG_CP
3 1 SPECIAL_FEATURE CHROME
4 1 SCHEDULE 80
5 2 BEARING_TYPE SB
6 2 SCHEDULE 80
7 3 ROT_DIR CCW
8 3 SPECIAL_FEATURE CATALOG_HSB
9 3 BEARING_TYPE SP
10 4 NDE_STYLE W_FAN
11 4 BEARING_TYPE SB
12 4 ROT_DIR CW*
You can do this with a self join:
select t.materialid, count(*) as nummatches
from t join
t tmat
on t.Charact = tmat.Charact and t.value = tmat.value
where tmat.materialid = #MaterialId
group by t.materialid
order by nummatches desc;
Notes:
You might want to remove the specified material, by adding where t.MaterialId <> tmat.MaterialId to the where clause.
If you want all materials, then make the join a left join and move the where condition to the on clause.
If you want only one material with the most matches, use select top 1.
If you want all materials with the most matches when there are ties, use `select top (1) with ties.

How to join these tables with conditional joins

I saw some useful tips in the web, however I still have some questions.
This is the "main" part of the new site we are creating, it is based on SQL SERVER 2012, the "TAREAS" table is the main key table, which has a self join. I found a way to search for the "tree" of the table, TAREA=TASK, Spanish to English, so basically it is a task manager, on which one task could be part of a primary task, or be a secondary task which can have more "child" tasks. I did it using Common table expressions.
the thing here is on the ID_TipoTarea (TaskType) on TAREAS table, can be on one specific type of task, for example on the diagram there are 2 types availables (but there are and will be more), TipoTareaDesarrollo or TipoTareaEventoSalon, the ID_TipoTarea cant be on both tables, so if ID_TipoTarea=1 then I join on TIpoTareaDesarrollo, if ID_TipoTarea=2 then I join on TipoTareaEventoSalon and so on ID_TipoTarea=3 to another table, and there will be more types, can you help me out?.
how can it be achieved using this query (this is the query to get all the levels on the main table, but I need the conditional joins).
with tareasCTE (id_tarea,id_tareaorigen,id_tipoTarea,nivel)
as(
select *,0 as nivel from tareas t
where id_tarea=#ID_Tarea
union all
select t2.*,nivel+1 from tareasCTE t
inner join tareas t2
on t.id_tarea=t2.id_tareaOrigen
)
I get this output
ID_Tarea, ID_TareaORigen, Nivel, ID_TipoTarea
3 NULL 0 null (no join)
4 3 1 1 (join this one with TipoTareaDesarrollo)
5 3 1 1 (join this one with TipoTareaDesarrollo)
6 3 1 3 (join this one with AnotherTable)
7 4 2 2 (join this one with TipoTareaEventoSalon)
8 4 2 2 (join this one with TipoTareaEventoSalon)
9 4 2 4 (join this one with AnotherTable2)
10 9 3 1 (join this one with TipoTareaDesarrollo)
11 9 3 1 (join this one with TipoTareaDesarrollo)
12 9 3 null (no Join)
13 12 4 1 (join this one with TipoTareaDesarrollo)
14 12 4 2 (join this one with TipoTareaEventoSalon)
15 12 4 2 (join this one with TipoTareaEventoSalon)
You can combine tables TipoTareaDesarrollo, TipoTareaEventoSalon, AnotherTable, AnotherTable2 into a single table using the UNION clause and package this in a second CTE as such:
WITH TipoAreasCTE as
(
SELECT * FROM TipoTareaDesarrollo
UNION
SELECT * FROM TipoTareaEventoSalon
UNION
SELECT * FROM AnotherTable
UNION
SELECT * FROM AnotherTable2
)
You can then join tareasCTE to TipoAreasCTE.
Note that the different tables in the UNION must have the same number of columns with the same datatypes; if not you must use a SELECT list and perhaps CAST the datatypes to make them similar.

Why does this query return "incorrect" results?

I have 3 tables:
'CouponType' table:
AutoID Code Name
1 CouT001 SunCoupon
2 CouT002 GdFriCoupon
3 CouT003 1for1Coupon
'CouponIssued' table:
AutoID CouponNo CouponType_AutoID
1 Co001 1
2 Co002 1
3 Co003 1
4 Co004 2
5 Co005 2
6 Co006 2
'CouponUsed' table:
AutoID Coupon_AutoID
1 2
2 3
3 5
I am trying to join 3 tables together using this query below but apparently I am not getting right values for CouponIssued column:
select CouponType.AutoID, Code, Name, Count(CouponIssued.CouponType_AutoID), count(CouponUsed.Coupon_AutoID)
from (CouponType left join CouponIssued
on (CouponType.AutoID = CouponIssued.CouponType_AutoID))
left join CouponUsed
on (couponUsed.Coupon_AutoID = CouponIssued.AutoID)
group by CouponType.AutoID, code, name
order by code
The expected result should be like:
**Auto ID Code Name Issued used**
1 CouT001 SunCoupon 3 2
2 CouT002 GdFriCoupon 3 1
3 CouT003 1for1Coupon 0 0
Thanks!
SELECT t.AutoID
,t.Code
,t.Name
,count(i.CouponType_AutoID) AS issued
,count(u.Coupon_AutoID) AS used
FROM CouponType t
LEFT JOIN CouponIssued i ON i.CouponType_AutoID = t.AutoID
LEFT JOIN CouponUsed u ON u.Coupon_AutoID = i.AutoID
GROUP BY 1,2,3;
You might consider using less confusing names for your table columns. I have made very good experiences with using the same name for the same data across tables (as far as sensible).
In your example, AutoID is used for three different columns, two of which appear a second time in another table under a different name. This would still make sense if Coupon_AutoID was named CouponIssued_AutoID instead.
change count(Coupon.CouponType_AutoID) to count(CouponIssued.CouponType_AutoID) and count(Coupon.Coupon_AutoID) to count(CouponUsed.Coupon_AutoID)