SQL server Query for Bill of materials quantity - sql

I have a bill of material (bom). Lets call this BOM 301755.
BOM 301755 is made of these parts
31161201 = need 1 pc of this
29975413 = need 2 pcs of this
299756 = need 2 pcs of this
And 305958 = need 1 pc of this
This would be level zero.
Now Lets focus on one of the part. Part: 29975413.
Part 29975413 is made of:
PLTSSL902 = 1pc
CAPSSL902 = 1pc
PIPSSL4SCH40 = 3.96
And LABSTR = 0.166
Now since we need 2 pcs of 29975413.
how can I do the query so it will show as follows:
PLTSSL902 = 1pc x 2 = 2 pc
CAPSSL902 = 1pc x 2 = 2 pc
PIPSSL4SCH40 = 3.96 x 2 = 7.92
And LABSTR = 0.166 x 2 = 0.332
I draw this for easier to read. :)
thank you

To make it a bit more general in its application I modified #dazedandconfused's answer a little bit:
;WITH bom as (
SELECT pid p,cid c, qty q, 0 bomlvl FROM #t WHERE pid='301755' // start id
UNION ALL
SELECT pid, cid, q*qty, bomlvl+1 FROM #t INNER JOIN bom ON c=pid
)
SELECT * from bom a WHERE NOT EXISTS (SELECT 1 FROM bom WHERE p=a.c)
This query calculates the BOM-level for each line and will list only list only those elements of the BOM that do not have children, regardless of how many levels your BOM might have. A fiddle can be found here.
My example will deliver the result:
p c q bomlvl
-------- ------------ ----- ------
305958 311620 4 1
305958 311620 0.1 1
299756 RDBSSL012 0.2 1
299756 RDBSSL012 6.834 1
29975413 PLTSSL902 2 1
29975413 CAPSSL4SCH40 2 1
29975413 PIPSSL4SCH40 7.92 1
29975413 LABSTR 0.332 1
31161201 PIPSSL2SCH40 4 1
You could go one step further and group the results by their c-id to get their total amounts used in a particular BOM. A table valued function would be the best way of writing this, where you pass the initial Id as a parameter. I cannot demonstrate this in my data.stackexchange-fiddle since functions cannot reference temporary tables, but the function definition should look more or less like this:
CREATE FUNCTION bomqty ( #pid varchar(20) ) RETURNS TABLE AS BEGIN
;WITH bom as (
SELECT pid p,cid c, qty q, 0 bomlvl FROM tbl WHERE pid=#pid
UNION ALL
SELECT pid, cid, q*qty, bomlvl+1 FROM tbl INNER JOIN bom ON c=pid
)
RETURN SELECT c item,sum(q) totalqty FROM bom a
WHERE NOT EXISTS (SELECT 1 FROM bom WHERE p=a.c)
GROUP BY c
END;
The function can then be used like any other table like this:
SELECT * FROM bomqty('301755')
This will get you
item totalqty
------------ --------
311620 4.1
CAPSSL4SCH40 2
LABSTR 0.332
PIPSSL2SCH40 4
PIPSSL4SCH40 7.92
PLTSSL902 2
RDBSSL012 7.034

You can use a recursive Common Table Expression to walk the hierarchy of parts, passing the quantity of the parent and multiplying the children's quantities by it.
DECLARE #bom int = 301755
CREATE TABLE #t(
BOM int,
KitID varchar(20),
SubAssy varchar(20),
BOMLevel int,
StdQty float
)
INSERT #t(BOM, KitID, SubAssy, BOMLevel, StdQty) VALUES
(301755, '301755', '31161201', 0, 1),
(301755, '301755', '29975413', 0, 2),
(301755, '301755', '299756', 0, 2),
(301755, '301755', '305958', 0, 1),
(301755, '305958', '311620', 1, 4),
(301755, '305958', '311620', 1, .1),
(301755, '299756', 'RDBSSL012', 1, .1),
(301755, '299756', 'RDBSSL012', 1, 3.417),
(301755, '29975413', 'PLTSSL902', 1, 1),
(301755, '29975413', 'CAPSSL4SCH40', 1, 1),
(301755, '29975413', 'PIPSSL4SCH40', 1, 3.96),
(301755, '29975413', 'LABSTR', 1, .166),
(301755, '31161201', 'PIPSSL2SCH40', 1, 4)
;WITH cte AS (
SELECT KitID, SubAssy, StdQty FROM #t WHERE KitID = #bom
UNION ALL
SELECT #t.KitID, #t.SubAssy, cte.StdQty * #t.StdQty FROM #t
INNER JOIN cte ON cte.SubAssy = #t.KitID
)
SELECT * FROM cte ORDER BY KitID, SubAssy

something like this should work:
CREATE TABLE #TableBom
(
Bom INT
,KitId INT
,SubAssy VARCHAR(20)
,BomLevel INT
,StdQty DECIMAL(10 ,3)
);
INSERT INTO #TableBom
SELECT 301755, 301755, '29975413', 0, 2
UNION ALL
SELECT 301755, 29975413, 'PLTSSL902', 1, 1
UNION ALL
SELECT 301755, 29975413, 'CAPSSL902', 1, 1
UNION ALL
SELECT 301755, 29975413, 'PIPSSL4SCH40',1,3.96
UNION ALL
SELECT 301755, 29975413, 'LABSTR', 1, 0.166
UNION ALL
SELECT 301755, 299756, 'RDBSSL012', 1, 3.147
SELECT b.Bom
,b2.SubAssy
,CONCAT(b2.SubAssy, ' = ' ,CAST(b2.StdQty AS DECIMAL(10,3)) ,' pc x ' ,CAST(b.StdQty AS DECIMAL(10,3)) ,' = ' ,CAST((b2.StdQty * b.StdQty) AS DECIMAL(10,2)) ,' pc') AS Calc
FROM #TableBom AS b
INNER JOIN #TableBom AS b2 ON b.SubAssy = CAST(b2.KitId AS VARCHAR(20));
Bom SubAssy Calc
301755 PLTSSL902 PLTSSL902 = 1.000 pc x 2.000 = 2.00 pc
301755 CAPSSL902 CAPSSL902 = 1.000 pc x 2.000 = 2.00 pc
301755 PIPSSL4SCH40 PIPSSL4SCH40 = 3.960 pc x 2.000 = 7.92 pc
301755 LABSTR LABSTR = 0.166 pc x 2.000 = 0.33 pc
EDIT:
if you only want to include the 29975413 you can include a WHERE b.SubAssy = '29975413'

Related

Common point in multiple polygons in SQL

i have two tables that contains a list geometry data
Ex. (0xE6100000010CFB24190B88E44A40AADDAB69817F3740)
i did the intersection of shapes between the two tables , now i'm trying to find a common point in all the intersected shapes
i tried to find the STCentroid() of each shape , but i can't find out how to find the common point in all of them
select p1.shape_data.STIntersection(p2.shape_data).STCentroid() as inter_geometry
from map_shapes p1
inner join areas_map_shapes p2 on p2.shape_data.STIntersects(p1.shape_data) = 1
where p2.shape_data.STIntersects(p1.shape_data) = 1
and p2.shape_id = 206
i tried also to aggregate all the intersected shapes
SELECT
geometry::UnionAggregate(ss.shape_data),
geometry::STGeomFromText( geometry::UnionAggregate(ss.shape_data).STCentroid().ToString(), 0).STY as lat,
geometry::STGeomFromText( geometry::UnionAggregate(ss.shape_data).STCentroid().ToString(), 0).STX as lon
FROM areas_map_shapes T
inner join map_shapes SS on SS.shape_data.STIntersects(T.shape_data) = 1
WHERE SS.shape_data.STIntersects(T.shape_data) = 1
AND T.shape_id = 206
and T.status = 1
and SS.status = 1
and T.country_id = 4
my problem is that i need to find the only one common point in all the shapes that intersects
adding image to represent what i got so far , this shows all the shapes the intersects with the main shape , i need to find a common point in all of them
Its hard to tell from your example because (as #nbk pointed out) its difficult to reproduce what you're asking for. That said, it looks like you're looking for the STIntersection function.
DECLARE #GeometryTable TABLE(
ID INT,
geom GEOMETRY
)
INSERT INTO #GeometryTable (ID, Geom) VALUES (1, GEOMETRY::STGeomFromText('POLYGON((0 0, 0 2, 2 2, 2 0, 0 0))', 0))
INSERT INTO #GeometryTable (ID, Geom) VALUES (2, GEOMETRY::STGeomFromText('POLYGON((1 1, 1 3, 3 3, 3 1, 1 1))', 0))
INSERT INTO #GeometryTable (ID, Geom) VALUES (3, GEOMETRY::STGeomFromText('POLYGON((0 1, 0 3, 2 3, 2 1, 0 1))', 0))
SELECT
G1.geom.STIntersection(G2.geom).STIntersection(G3.geom)
FROM
#GeometryTable G1
INNER JOIN
#GeometryTable G2
ON
G1.geom.STIntersects(G2.geom) = 1
INNER JOIN
#GeometryTable G3
ON
G1.geom.STIntersects(G3.geom) = 1
AND G2.geom.STIntersects(G3.geom) = 1
WHERE
G1.ID = 1
AND G2.ID = 2
AND G3.ID = 3
Not sure there's an easy/fast way to do it. One idea is to use STIntersection to create a intersection polygon of all your areas in a recursive CTE:
drop table #t_geoms
create table #t_geoms (geom geometry, row_id int identity)
-- create some random data
insert into #t_geoms
select top 30 GEOMETRY::Point(ROW_NUMBER() OVER(ORDER BY object_id) * 0.01 + 10,ROW_NUMBER() OVER(ORDER BY object_id) * 0.01 + 10, 4326).STBuffer(3) x
from sys.objects
;with cte as (
select geom, row_id
from #t_geoms
where row_id = 1
union all
select g.geom.STIntersection(c.geom), g.row_id
from cte c
inner join #t_geoms g
ON g.row_id = c.row_id + 1
)
select top 1 geom, geom.STCentroid() AS centerPointOfIntersection
from cte
order by row_id desc
option(MAXRECURSION 0)
Note that if not all polygons actually intersect, you get an emptry geom

SQL Server loop through a table for every 5 rows

I need to write a stored procedure or table function to return a new data table as a new data source.
I wish to loop through the original table for every 5 rows base on the invoice ID column (it's possible not start from 1), the first 5 rows add to the left of the new table and the second 5 rows add to the right of the new table, the third 5 rows to the left and so on.
For example, Here is the original table:
Here is the expect table:
Thanks in advance!
declare #rowCount int = 5;
with cte as (
select *,( (IN_InvoiceID-1) / #rowCount ) % 2 group1
,( (IN_InvoiceID-1) / #rowCount ) group2
,IN_InvoiceID % #rowCount group3
from T
)
select * from cte
select T1.INID,T1.IN_InvoiceID,T1.IN_InvoiceAmount,T2.INID,T2.IN_InvoiceID,T2.IN_InvoiceAmount
from CTE T1
left join CTE T2 on T2.group1 = 1 and T1.group2 = T2.group2-1 and T1.group3 = T2.group3
where T1.group1 = 0
Test DDL
CREATE TABLE T
([INID] varchar(38), [IN_InvoiceID] int, [IN_InvoiceAmount] int)
;
INSERT INTO T
([INID], [IN_InvoiceID], [IN_InvoiceAmount])
VALUES
('DB3E17E6-35C5-41:121-93B1-F809BF6B2972', 1, 2999),
('3212F048-8213-4FCC-AB64-121485B77D4E43', 2, 3737),
('E3526373-A204-40F5-801C-7F8302A4E5E2', 3, 3175),
('76CC9C19-BF79-4E8A-8034-A33805AD3390', 4, 391),
('EC7A2FBC-B62D-4865-88DE-A8097975F125', 5, 1206),
('52AD3046-21331-4F0A-BD1D-67F232C54244', 6, 402),
('CA48F132-A9F5-4516-9E58-CDEE6644AAD1', 7, 1996),
('02E10C31-CAB2-4220-B66A-CEE5E67A9378', 8, 3906),
('98F1EEFF-B07A-4B65-87F4-E165264284DD', 9, 2575),
('91EBDD8B-B73C-470C-8900-DD66078483DB', 10, 2965),
('6E2490E5-C4DE-4833-877F-1590F7BDC1B8', 11, 1603),
('00985921-AC3C-4E3E-BAE1-7F58302F831A', 12, 1302)
;
Result:
Could you please check article Display Data in Multiple Columns using SQL showing with example case how a database developer can show the list of data rows in a columnar mode using Row_Number() function and mode arithmetic expression
You need to add additional columns from the same row that is different in the sample
Seems as if you want to split the table into 2 tables with alternating 5 rows. An easy way to do this would be:
Take data into a temp table having an extra column (lets say
grouping_id)
Update the grouping id so that each 5 rows have the same id. You can
use in_invoiceId % 5 (the nod function). After this step the first 5
rows will have grouping_id 0, next 5 will have 1, next will have 2
(assuming your invoice id is incremented +1 for all rows).
You can just do a normal select with where clause for odd and even grouping_id
Ideally, you can manage with the 2 tables Master and detail table.
But due to my curiosity, I am able to solve and give the answer as
Declare #table table(id int identity, invoice_id int)
; WITH Numbers AS
(
SELECT n = 1
UNION ALL
SELECT n + 1
FROM Numbers
WHERE n+1 <= 50
)
insert into #table SELECT n
FROM Numbers
Select (a.id )%5 ,* from #table a join #table b on a.id+5 = b.id and a.id != b.id
;WITH Numbers AS
(
SELECT n = 1, o = 5
UNION ALL
SELECT n + 10, o = o+10
FROM Numbers
WHERE n+1 <= 50
)
select a.id ParentId,a.invoice_id ParentInvoiceId, --b.n, b.o,
c.invoice_id childInvoiceID from #table a
join Numbers b on a.id between b.n and b.o
left join #table c on a.id + 5 = c.id
Here is my solution
First i create grps based on whether the in_invoiceid is divisible by 5 or not.(Ignore the remainders)
After that i create a category to indicate between alternative groups(ie by checking if the remainder is 0 or otherise)
Then its a matter of dense_ranking the records on the basis of the category field ordered by in_invoiceid
Lastly a join with category=1 rows with same dense_rank as those records in category=0
create table Invoicetable(IN_ID varchar(100), IN_InvoiceID int)
INSERT INTO Invoicetable (IN_ID, IN_InvoiceID)
VALUES
('2345-BCDE-6645-1DDF', 1),
('2345-BCDE-6645-3DDF', 2),
('2345-BCDE-6645-4DDF', 3),
('2345-BCDE-6645-5DDF', 4),
('2345-BCDE-6645-6DDF', 5),
('2345-BCDE-6645-7DDF', 6),
('2345-BCDE-6645-aDDF', 7),
('2345-BCDE-6645-sDDF', 8),
('2345-BCDE-6645-dDDF', 9),
('2345-BCDE-6645-dDDF', 10),
('2345-BCDE-6645-dDDF', 11),
('2345-BCDE-6645-dDDF', 12);
with data
as (
select *
,(in_invoiceid-1)/5 as grp
,case when ((in_invoiceid-1)/5)%2=0 then '1' else '0' end as category
,dense_rank() over(partition by case when ((in_invoiceid-1)/5)%2=0 then '1' else '0' end
order by in_invoiceid) as rnk
from invoicetable a
)
select *
from data a
left join data b
on a.rnk=b.rnk
and b.category=0
where a.category=1
Here is db fiddle link.
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=287f101737c580ca271940764b2536ae
You may try with the following approach. Dividing the table is done with (((ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) - 1) / 5) % 2 = 0) which groups records in left and right groups.
CREATE TABLE #InvoiceTable(
IN_ID varchar(24),
IN_InvoiceID int
)
INSERT INTO #InvoiceTable (IN_ID, IN_InvoiceID)
VALUES
('2345-BCDE-6645-1DDF', 1),
('2345-BCDE-6645-3DDF', 2),
('2345-BCDE-6645-4DDF', 3),
('2345-BCDE-6645-5DDF', 4),
('2345-BCDE-6645-6DDF', 5),
('2345-BCDE-6645-7DDF', 6),
('2345-BCDE-6645-aDDF', 7),
('2345-BCDE-6645-sDDF', 8),
('2345-BCDE-6645-dDDF', 9),
('2345-BCDE-6645-dDDF', 10),
('2345-BCDE-6645-dDDF', 11),
('2345-BCDE-6645-dDDF', 12);
WITH cte AS (
SELECT
IN_ID,
IN_InvoiceID,
CASE
WHEN (((ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) - 1) / 5) % 2 = 0) THEN 'L'
ELSE 'R'
END AS IN_Position
FROM #InvoiceTable
),
cteL AS (
SELECT IN_ID, IN_InvoiceID, ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) AS IN_RowNumber
FROM cte
WHERE IN_Position = 'L'
),
cteR AS (
SELECT IN_ID, IN_InvoiceID, ROW_NUMBER() OVER (ORDER BY IN_InvoiceID) AS IN_RowNumber
FROM cte
WHERE IN_Position = 'R'
)
SELECT cteL.IN_ID, cteL.IN_InvoiceID, cteR.IN_ID, cteR.IN_InvoiceID
FROM cteL
LEFT JOIN cteR ON (cteL.IN_RowNumber = cteR.IN_RowNumber)
Output:
IN_ID IN_InvoiceID IN_ID IN_InvoiceID
2345-BCDE-6645-1DDF 1 2345-BCDE-6645-7DDF 6
2345-BCDE-6645-3DDF 2 2345-BCDE-6645-aDDF 7
2345-BCDE-6645-4DDF 3 2345-BCDE-6645-sDDF 8
2345-BCDE-6645-5DDF 4 2345-BCDE-6645-dDDF 9
2345-BCDE-6645-6DDF 5 2345-BCDE-6645-dDDF 10
2345-BCDE-6645-dDDF 11 NULL NULL
2345-BCDE-6645-dDDF 12 NULL NULL

SQL query with subqueries based on base values

Good morning; I have two tables that I am trying to work with to develop a single query with subquery and having issues if someone could help.
First Table - [MPA_Desc] - There is more data I parsed for example
MPAID Color Model Side
1 085 x1 R
2 777 x1 R
3 085 x1 L
4 777 x1 L
Second Table - [Paintsched] - There is more data I parsed for example
Lot MPAID Amount
1 1 100
2 2 250
3 4 100
4 2 100
I am trying to get this as my query result:
Color R L
085 100 0
777 350 100
This is the query and subquery I am using, It's incomplete and doesn't have the "LH" calculations in it because I want to get the RH right first:
Select distinct(mp.Color), rh.RH
from MPA_Desc MP
right join (Select MPA_Desc.MPAID, MPA_Desc.Color, nullif(sum(qty),0) as RH from PaintSched inner join MPA_Desc on PaintSched.MPAID = MPA_Desc.MPAID
where side = 'r' group by MPA_Desc.MPAID,MPA_Desc.Color) RH
ON mp.MPAID = rh.MPAID
where Model = 'x1'
But this however gives me the following result:
Color R
085 100
085 NULL
777 350
777 Null
I know it's from my full join but I need to show the Null's as "0" and I thought nullif would work but apparently not in a subquery. Secondly I need it to just show the List of color codes by model in the original query and seperate out the sums based on RH or LH parts. Any help would be great, thank you all!
You can use PIVOT
DECLARE #MPA_Desc TABLE (MPAID INT, Color VARCHAR(5), Model VARCHAR(5), Side VARCHAR(5))
INSERT INTO #MPA_Desc VALUES
(1 , '085', 'x1', 'R'),
(2 , '777', 'x1', 'R'),
(3 , '085', 'x1', 'L'),
(4 , '777', 'x1', 'L')
DECLARE #Paintsched TABLE (Lot INT, MPAID INT, Amount INT)
INSERT INTO #Paintsched VALUES
(1 ,1 , 100),
(2 ,2 , 250),
(3 ,4 , 100),
(4 ,2 , 100)
SELECT Color, ISNULL(R,0) R, ISNULL( L,0) L FROM
( SELECT Color, Side, Amount FROM #MPA_Desc D
INNER JOIN #Paintsched P ON D.MPAID = P.MPAID ) SRC
PIVOT (SUM(Amount) FOR Side IN ([R],[L])) PVT
Result:
Color R L
----- ----------- -----------
085 100 0
777 350 100
You need conditional aggregation :
select md.Color,
sum(case when Side = 'R' then ps.amount else 0 end),
sum(case when Side = 'L' then ps.amount else 0 end)
from MPA_Desc md inner join
Paintsched ps
on ps.MPAID = md.MPAID
group by md.Color;
Try the following (Its Dynamic) -:
Declare #sql varchar(MAX),#query nvarchar(MAX)
select #sql=stuff((select distinct ', sum(case when side='''+side+''' then amount else 0 end) as '+side from [MPA_Desc] FOR XML PATH ('')), 1, 1, '' )
set #query='select color,'+#sql+' from [MPA_Desc] m join [Paintsched] n on m.MPAID=n.MPAID group by color'
EXEC sp_sqlexec #query
SQL Server 2014

Create table of unique values from join

I have two tables with various addresses. One is a table of locations we already have on file, the other is new business. The idea is that I'm joining these two tables on their co-ordinates in order to show if we have a clash between the new business and the current business.
What I have found is that in the new business we have one location that matches three locations that we already have.
When I do my simple inner join I get back 3 records when really I want to display the 4 (1 from new, 3 from current). I have tried other joins and union as well as sub queries but with no luck. I know there is a way but just can't figure it out.
SELECT *
FROM NewBusiness
INNER JOIN Live L ON N.Latitude = L.Latitude AND N.Longitude = L.Longitude
Thanks in advance
Maybe not the best answer (it catch duplicate rows in the same table) but still working correctly
select distinct 'oldbiz', l.id, l.latitude, l.longitude
from Live l, NewBusiness n
where l.latitude = n.latitude
and l.longitude = n.longitude
union all
select distinct 'newbiz', n.id, n.latitude, n.longitude
from Live l, NewBusiness n
where l.latitude = n.latitude
and l.longitude = n.longitude
SQLFIDDLE
So I was able to get what I think you are looking for using a union. It may not be the best way to do it but it looks like it works. I made a SQL Fiddle to show it.
You can see the fiddle here: SQLFIDDLE
to test, I created two tables, Live and NewBusiness.
Created them like this
CREATE TABLE Live
([ID] varchar(1), [latitude] int, [longitude] int)
;
INSERT INTO Live
([ID], [latitude], [longitude])
VALUES
('a', 1, 2),
('b', 1, 2),
('c', 1, 2),
('d', 4, 3),
('e', 5, 4),
('k', 5, 7),
('l', 5, 9),
('M', 5, 7)
;
and
CREATE TABLE NewBusiness
([ID] varchar(1), [latitude] int, [longitude] int)
;
INSERT INTO NewBusiness
([ID], [latitude], [longitude])
VALUES
('f', 1, 2),
('g', 5, 2),
('h', 1, 8),
('i', 6, 3),
('z', 5, 7),
('y', 12, 4),
('x', 5, 7)
;
The query I used was
(
SELECT L.ID
,L.Latitude
,L.Longitude
FROM Live L
INNER JOIN NEWBUSINESS N
ON L.Latitude = N.Latitude AND L.Longitude = N.Longitude
)
UNION
(
SELECT N.ID
,N.LATITUDE
,N.LONGITUDE
FROM NEWBUSINESS N
INNER JOIN Live L
ON N.Latitude = L.Latitude AND N.Longitude = L.Longitude
GROUP BY N.ID
,N.LATITUDE
,N.LONGITUDE
)
The first part of the union gets all the things in Live that have matches in Newbusiness. The second part of the union gets all the things in Newbusiness that have matches in Live. The results are then union'd together.
Tables:
Live
ID latitude longitude
a 1 2
b 1 2
c 1 2
d 4 3
e 5 4
k 5 7
l 5 9
M 5 7
**NewBusiness**
ID latitude longitude
f 1 2
g 5 2
h 1 8
i 6 3
z 5 7
y 12 4
x 5 7
**Query Results**
ID Latitude Longitude
a 1 2
b 1 2
c 1 2
f 1 2
k 5 7
M 5 7
x 5 7
z 5 7
Does something like this give you what you need (it will count once for each record in either table that has a match in the other table):
WITH cte1
AS
(
SELECT
N.latitude
, N.longitude
, ROW_NUMBER() OVER (ORDER BY N.latitude, N.longitude) r1
FROM NewBusiness N
)
,
cte2
AS
(
SELECT
N.latitude
, N.longitude
, ROW_NUMBER() OVER (PARTITION BY N.r1 ORDER BY N.latitude, N.longitude) r2
FROM
cte1 N
JOIN Live L ON
N.Latitude = L.Latitude
AND N.Longitude = L.Longitude
)
SELECT
latitude
, longitude
, 'NewBusiness' sourceTable
FROM cte2
WHERE r2 = 1
UNION ALL
SELECT
latitude
, longitude
, sourceTable
FROM
(
SELECT DISTINCT
latitude
, longitude
, r2
, 'Live' sourceTable
FROM cte2
) Q

SQL sub query with complex criteria

I have a table like this:
TransId. LayerNo. AccountId.
100. 1. 2.
100. 2. 3.
120. 1. 5.
120. 2. 6.
120. 3. 12.
70. 1. 2.
I want to find transId(s) where:
(LayerNo = 1 and (accountId = 2 or 5))
and
(LayerNo = 2 and (accountId = 3 or 6))
And result set would be row no 1,2,3,4.
How could I write query to get the result?
My database is SQL server 2008 r2
Thanks in advance
Nima
SELECT TransId
FROM your_table
WHERE ( layerno = 1
AND accountid IN ( 2, 5 ) )
INTERSECT
SELECT TransId
FROM your_table
WHERE ( layerno = 2
AND accountid IN ( 3, 6 ) )
One approach is to ensure that each transID must have two records that satisfy the conditions you outlined.
SELECT * FROM
TABLE
WHERE TransID IN(
SELECT TransId
FROM table
WHERE ( layerno = 1
AND accountid IN ( 2, 5 ) )
OR ( layerno = 2
AND accountid IN( 3, 6 ) )
GROUP BY
TransId
HAVING Count(*) = 2
)
However this could be a problem if you can have multple records where layerno = 1. So you can use self joins instead to ensure the criteria.
SELECT DISTINCT a.transid
FROM table a
INNER JOIN table b
ON a.transid = b.transid
INNER JOIN table c
ON a.transid = c.transid
WHERE b.layerno = 1
AND accountid IN ( 2, 5 )
AND c.layerno = 2
AND accountid IN ( 3, 6 )
That said Martin's INTERSECT approach is probably the best
Do you mean:
SELECT
TransId,
LayerNo,
AccountId
FROM Table
WHERE (LayerNo = 1 AND AccountId IN (2, 5)) OR
(LayerNo = 2 AND AccountId IN (3, 7))
create table #temp
( rowId Int Identity(1,1), transId int)
INSERT INTO #temp(transId)
select TransId
from TableName
where (layerNo = 1 and accountID IN (2, 5))
OR (layerNo = 2 and accountId IN (3, 6))
select * from #temp
SELECT
base.TransId,
base.LayerNo,
base.AccountId
FROM TableX AS base
JOIN TableX AS a
ON a.TransId = base.TransId
AND a.LayerNo = 1 AND a.AccountId IN (2, 5)
JOIN TableX AS b
ON b.TransId = base.TransId
AND b.LayerNo = 2 AND b.AccountId IN (3, 7)
WHERE (base.LayerNo = 1 AND base.AccountId IN (2, 5))
OR (base.LayerNo = 2 AND base.AccountId IN (3, 7))
This intersection is empty. If you take the values where LayerNo = 1 and LayerNo = 2 and intersect them their intersection is empty because these events are mutually exclusive. I believe this error comes from how the question was originally stated. I might be wrong but the predicate should have been
(LayerNo = 1 and (accountId = 2 or 5)) OR (LayerNo = 2 and (accountId = 3 or 6))
Replace the AND with an OR. If the predicate was stated correctly then the intersect is correct but will always be empty.
SELECT *
FROM table
WHERE (LayerNo = 1 AND (AccountID = 2 OR AccountID = 5))
OR (LayerNo = 2 AND (AccountID = 3 OR AccountID = 6))