SQL Select based on condition from another table - sql

I have two tables composing with the following definitions:
A
ID | Name | Description
1 | Bag | It's a bag
2 | Leather | Leather makes the bag
3 | Website | It's a website
4 | Images | Just normal images
5 | Text | Some descriptive text
B
parentID | childID | Quantity
1 | 2 | 5
3 | 4 | 2
3 | 5 | 1
I'm trying to find all items in table A which are of level 0 (meaning they have no parent) and then use that to try and find the items in the following levels.
So far I've gotten to this, which I thought would work, but clearly not...
SELECT *
FROM A
WHERE A.ID in (
SELECT B.parentID
FROM B
WHERE (B.childID is NULL)
);
If anyone could shed some light on this I'd really appreciate it. Also if there's some must-reads for SQL querying please let me know, I'd be glad to learn more about this, as it's something I struggle with.
Thanks in advance.
EDIT:
I've edited the data to hopefully illustrate a little better what I'm trying to achieve. There's several items, some are parents and some are children.
From table B you can tell that to make 1 bag, you need 5 leather (bag is parent of leather with quantity 5). Or that the website is composed of 2 Image and 1 Text (website is parent to both Image quantity 2 and Text quantity 1).
I want to query for the bag and website only, meaning the items in table A which have no parents in table B.

Found it myself, basically what I was looking for was this:
SELECT *
FROM A
WHERE A.ID not in (
SELECT B.childID
FROM B
);
This selects all items from A which have no parents (meaning they are not children of any other item).

Related

Trying to find non-duplicate entries in mostly identical tables(access)

I have 2 different databases. They track different things about inventory. in essence they share 3 common fields. Location, item number and quantity. I've extracted these into 2 tables, with only those fields. Every time I find an answer, it doesn't get all the test cases, just some of the fields.
Items can be in multiple locations, and as a turn each location can have multiple items. The primary key would be location and item number.
I need to flag when an entry doesn't match all three fields.
I've only been able to find queries that match an ID or so, or who's queries are beyond my comprehension. in the below, I'd need a query that would show that rows 1,2, and 5 had issues. I'd run it on each table and have to verify it with a physical inventory.
Please refrain from commenting on it being silly having information in 2 different databases, All I get in response it to deal with it =P
Table A
Location ItemNum | QTY
-------------------------
1a1a | as1001 | 5
1a1b | as1003 | 10
1a1b | as1004 | 2
1a1c | as1005 | 15
1a1d | as1005 | 15
Table B
Location ItemNum | QTY
-------------------------
1a1a | as1001 | 10
1a1d | as1003 | 10
1a1b | as1004 | 2
1a1c | as1005 | 15
1a1e | as1005 | 15
This article seemed to do what I wanted but I couldn't get it to work.
To find entries in Table A that don't have an exactly matching entry in Table B:
select A.*
from A
left join B on A.location = B.location and A.ItemNum = B.ItemNum and A.qty = B.qty
where B.location Is Null
Just swap all the A's and B's to get the list of entries in B with no matching entry in A.

NOT LIKE search on Link Table

I have a Model table with an ID and A Text Column:
ID | Description
=======================
1 | Model A
2 | Model B
3 | Model C
I have an Items table with an ID and lots of other columns. These two tables are linked via an intermediary table call ItemModels with the following data:
ID | ItemID | ModelID
==================================
1 | 1 | 1
2 | 1 | 2
3 | 2 | 1
4 | 2 | 2
5 | 2 | 3
6 | 3 | 2
I want to search using the standard "Contains, Does Not Contain, Starts With, Ends With" methods.
If I do a "Contains", "Starts With" or "Ends With" search using the LIKE operator this works fine and I always get the correct results, however I have a problem when using the NOT LIKE operator:
If I want to return all items where the model description does not contain "C" (case insensitive) I thought simply of doing the following:
SELECT ItemID FROM ItemModels INNER JOIN Model ON ItemModels.ModelID = Model.ID WHERE Description NOT LIKE '%C%'
I want this query to return Items 1 and 3 as neither of them have any models that contain 'C' however this query will also return item 2 as it will hit the record with ItemModel.ID = 3 and say "That does not contain C so we want to return that!" which of course is undesired behaviour.
So my question is:
How can I do a NOT LIKE search that encompasses all records in a Link table?
ps. I hope I have made this clear as it took me hours to track this issue down and work out why it was happening. And even more hours trying to work out how the hell to fix it!
You don't want any of the items to match your condition. Think in terms of aggregation and a having clause:
SELECT im.ItemID
FROM im.ItemModels im INNER JOIN
Model m
ON im.ModelID = m.ID
GROUP BY im.ItemId
HAVING SUM(CASE WHEN Description LIKE '%C%' THEN 1 ELSE 0 END) = 0;
This query counts the number of models that match the item. The = 0 says that there are none. I like this approach because it is quite flexible. Using AND and OR you can put together complicated conditions, such as like '%a%' and '%b%' but not like '%c%'.

Grouping issue in SSRS

So I have a table that has divisions and sub-divisions. The structure is as follows:
COMPANYID | DIVISIONID | DIVISION | PARENTID
1 | 1 | North | NULL
1 | 2 | South | NULL
1 | 3 | East | NULL
1 | 4 | West | NULL
1 | 5 | Sales | 1
1 | 6 | Service | 1
1 | 7 | Sales | 2
1 | 8 | Service | 2
1 | 9 | Sales | 3
1 | 10 | Service | 3
1 | 11 | Sales | 4
1 | 12 | Service | 4
The idea behind the table is that the data would indicate all of those divisions/subdivisions are the for the same company. Anything with a null PARENTID is a "Division" and anything that has a PARENTID is a "Sub-Division". There will be a max of 2 tiers (Division --> Subdivision). This table would create the following hierarchy:
Company 1
North
Sales
Service
South
Sales
Service
East
Sales
Service
West
Sales
Service
I am trying to make an SSRS report that shows details about the company including a table with all the divisions and sub-divisions. I am using the following query to retrieve a list of all the divisions and subdivisions:
SELECT division, parentid
FROM division
WHERE companyid=#companyid
#companyid is a parameter for the report. My thought process is that I want create a table that has a Parent group and a Detail group where the Parent group would be all those rows that have a null PARENTID and then the Detail group would list all rows whose PARENTID equals the divisionid of Parent row. I'm just not sure how to set that up in the groups. Filters? Separate queries? Do it in the SQL query?
Any and all help is greatly appreciated! Thanks!
For a simple hierarchy with a maximum of 2 levels you can just join the table on itself:
SELECT D1.CompanyId, D1.DivisionId, D1.Division, D2.Division AS Subdivision
FROM DivisionTable D1
INNER JOIN DivisionTable D2 ON D1.DivisionId = D2.ParentId AND D1.CompanyId = D2.CompanyId
WHERE D1.CompanyId = #CompanyId
If i understood your goal correctly, you want the report to display the information in hierarchy mode.
If so you can achieve it with setting a Recursive Hierarchy Group.
Reporting services has the ability to display "hierarchy" information in a "tree" layout
For more information look here
You don't have to create another query the first one that has the CompanyID And the ParentID is just fine
You just need your select statement as a dataset and SSRS can do the grouping for you. You can even tier on multiple levels or better yet collapse and expand your groupings based on filters.
How to get started:
Create your dataset as you have shown
Put in a table object from the toolbox
Only put in the columns of detail data
In the lower left of the 'Design' layout click the 'detail's and click 'add Group>Parent Group'. Select your grouping
If you want you can 'collapse' on demand the details columns. You can choose 'Visibility' on the detail grouping. 'When the report is initially run'>Choose 'Hide'. Check 'Display can be toggled by this report item': Select a cell reference to what you want to expand the details with.

Advance Query with Join

I'm trying to convert a product table that contains all the detail of the product into separate tables in SQL. I've got everything done except for duplicated descriptor details.
The problem I am having all the products have size/color/style/other that many other products contain. I want to only have one size or color descriptor for all the items and reuse the "ID" for all the product which I believe is a Parent key to the Product ID which is a ...Foreign Key. The only problem is that every descriptor would have multiple Foreign Keys assigned to it. So I was thinking on the fly just have it skip figuring out a Foreign Parent key for each descriptor and just check to see if that descriptor exist and if it does use its Key for the descriptor.
Data Table
PI Colo Sz OTHER
1 | Blue | 5 | Vintage
2 | Blue | 6 | Vintage
3 | Blac | 5 | Simple
4 | Blac | 6 | Simple
===================================
Its destination table is this
===================================
DI Description
1 | Blue
2 | Blac
3 | 5
4 | 6
6 | Vintage
7 | Simple
=============================
Select Data.Table
Unique.Data.Table.Colo
Unique.Data.Table.Sz
Unique.Data.Table.Other
=======================================
Then the dual part of the questions after we create all the descriptors how to do a new query and assign the product ID to the descriptors.
PI| DI
1 | 1
1 | 3
1 | 4
2 | 1
2 | 3
2 | 4
By figuring out how to do this I should be able to duplicate this pattern for all 300 + columns in the product. Some of these fields are 60+ characters large so its going to save a ton of space.
Do I use a Array?
Okay, if I understand you correctly, you want all unique attributes converted from columns into rows in a single table (detailstable) that has an id and a description field:
Assuming the schema:
datatable
------------------
PI [PK]
Colo
Sz
OTHER
detailstable
------------------
DI [PK]
Description
You can first get all of the unique attributes into its own table with:
INSERT INTO detailstable (Description)
SELECT
a.description
FROM
(
SELECT DISTINCT Colo AS description
FROM datatable
UNION
SELECT DISTINCT Sz AS description
FROM datatable
UNION
SELECT DISTINCT OTHER AS description
FROM datatable
) a
Then to link up the datatable to the detailstable, I'm assuming you have a cross-reference table defined like:
datadetails
------------------
PI [PK]
DI [PK]
You can then do:
INSERT INTO datadetails (PI, DI)
SELECT
a.PI
b.DI
FROM
datatable a
INNER JOIN
detailstable b ON b.Description IN (a.Colo, a.Sz, a.OTHER)
I reckon you want to split description table for different categories, like - colorDescription, sizeDescription etc.
If that is not practical then I would recommend having an extra column showing an category attribute:
DI Description Category
1 | Blue | Color
2 | Blac | Color
3 | 5 | Size
4 | 6 | Size
6 | Vintage | Other
7 | Simple | Other
And then have primary key in this table as combination of ID and Category column.
This will have less chances for injecting any data errors. It will be also easy to track that down.

How to get all children of a node in tree structure ? SQL query?

table - user
columns -
(userId ,name, managerId)
rows -
(1,nilesh,0)
(2,nikhil,1)
(3,nitin ,2)
(4,Ruchi,2)
if I give id of user it should list all reporting people to him .
if I give userId = 2 it should return 3,4.
Is this query correct
SELECT ad3.userId
FROM user au , user au2 , user au3
WHERE
ad.managerId = ad2.managerId AND
ad3.managerId = ad2.userId AND
ad.userId=2
Is there any efficent way to manage tree structure in DB ?
How about right and left leaf way ?
I use a text field to deal with trees in SQL. It's easier than using left/right values.
Lets take the example from the MySQL article:
+-----------------------+
| name |
+-----------------------+
| ELECTRONICS |
| TELEVISIONS |
| TUBE |
| LCD |
| PLASMA |
| GAME CONSOLES |
| PORTABLE ELECTRONICS |
| MP3 PLAYERS |
| FLASH |
| CD PLAYERS |
| 2 WAY RADIOS |
| FRS |
+-----------------------+
It would result in a table like this:
Id ParentId Lineage Name
1 null /1/ ELECTRONICS
2 1 /1/2/ TELEVISIONS
3 2 /1/2/3/ TUBE
4 2 /1/2/4/ LCD
5 2 /1/2/5/ PLASMA
6 6 /1/6/ GAME CONSOLES
7 1 /1/7/ PORTABLE ELECTRONICS
8 7 /1/7/8/ MP3 PLAYERS
9 8 /1/7/8/9/ FLASH
10 7 /1/7/10/ CD PLAYERS
11 1 /1/11/ 2 WAY RADIOS
12 11 /1/11/12/ FRS
Do find all portables you simply use the Lineage from portables:
SELECT * FROM theTable WHERE Lineage LIKE '/1/7/%'
Cons:
You need to do a UPDATE after each INSERT to append PK to Lineage
Suggestion:
I usally add another column where I put the path as text in (for instance 'electronics/televisions/tube')
Something like this (ANSI SQL):
WITH RECURSIVE emptree (userid, name, managerid) AS (
SELECT userid,
name,
managerid
FROM the_table
WHERE userid = 2
UNION ALL
SELECT c.userid,
c.name,
c.managerid
FROM the_table c
JOIN emptree p ON p.userid = c.managerid
)
SELECT *
FROM emptree
In my opinion, the problem with the adjacency list model is that it gets difficult to deal with in SQL especially when you don't know how deeply nested your tree structure is going to be.
The 'left and right leaf way' you mention is probably the nested set model and allows you to store things like this
LFT RGT Name
1 8 nilesh
2 7 nikhil
3 4 nitin
5 6 Ruchi
Then you can find all of anyones subordinates by simply
SELECT Name FROM Hierarchy WHERE LFT BETWEEN #LFT AND #RGT
I think it is much easier to deal with for querying but is harder to do for tree modifications. If your data doesn't change much then I think this is a much better solution. (Not everyone will agree with me though)
There is a Very good Tutorial here
I have a simple answer to this question:
Table creation:
Create Table #AllChilds(id int)
List item:
Declare #ParentId int;
set #ParentId=(Select Id from Employee Where id=1)
-- Here put the id as the record for which you want all childs
While(#ParentId is not null )
begin
set #ParentId=(Select Id from Employee Where ParentId=#ParentId)
insert into #AllChilds values(#ParentId)
end
See the result:
Select Id from #AllChilds
Cleanup:
Drop table #AllChilds
SELECT user.id FROM user WHERE user.managerid = 2
Is this what you want?