Comparing 2 tables with Limited join options - sql

I have this category group , which has 5 categories and Description(Display Text)of the categories . Fields(Category ID, Display Text)
And I have another view which has information about like Month,Gender,New(employee in Department),Transfer(Employee From another department) and Continuing(Same department) and Category ID .
Now I want to check if for each categoryID in the category table, if there are any New,transfer or Continuing according to the Gender and month.
For example, CategoryID =1 , Check in the view if there is any CategoryID=1 present then return all the details of the ID in the VIEW , If not just return 0 for every Gender and Month.
#EDIT:
select c.UniqueID,
c.DisplayText ,
s.Gender,
s.Term,
s.Status ,
s.NoofStatus
from Category c
left outer Join
Status_Count_View s on c.UniqueID=s.UniqueID
I want all the Records in Table A to be mapped on to Table B for every Term.. If no records in Table B are found for a particular Category in Table A still that Category has to be displayed with a value 0

select * from category c, cat_view cv where c.Cat_ID = cv.categoryID
union
select c.cat_id, c.DisplayText, 0 , 0 ,'',0,0,0
from category c
where c.Cat_ID not in (select distinct(categoryID) from cat_view)
Please edit column names according to your need. Key here is 'union'. First we get everything that matches. Then we get all category which doesn't match and add those into result (achieved by 'union' keyword).

Related

SQL Inner join in a nested select statement

I'm trying to do an inner join in a nested select statement. Basically, There are first and last reason IDs that produce a certain number (EX: 200). In another table, there are definitions for the IDs. I'm trying to pull the Last ID, along with the corresponding comment for whatever is pulled (EX: 200 - Patient Cancelled), then the first ID and the comment for whatever ID it is.
This is what I have so far:
Select BUSN_ID
AREA_NAME
DATE
AREA_STATUS
(Select B.REASON_ID
A.LAST_REASON_ID
FROM BUSN_INFO A, BUSN_REASONS B
WHERE A.LAST_REASON _ID=B.REASON_ID,
(Select B.REASON_ID
A. FIRST_REASON_ID
FROM BUSN_INFO A, BUSN_REASONS B
WHERE A_FIRST_REASON_ID = B.REASON_ID)
FROM BUSN_INFO
I believe an inner join is best, but I'm stuck on how it would actually work.
Required result would look like (this is example dummy data):
First ID -- Busn Reason -- Last ID -- Busn Reason
1 Patient Sick 2 Patient Cancelled
2 Patient Cancelled 2 Patient Cancelled
3 Patient No Show 1 Patient Sick
Justin_Cave's SECOND example is the way I used to solve this problem.
If you want to use inline select statements, your inline select has to select a single column and should just join back to the table that is the basis of your query. In the query you posted, you're selecting the same numeric identifier multiple times. My guess is that you really want to query a string column from the lookup table-- I'll assume that the column is called reason_description
Select BUSN_ID,
AREA_NAME,
DATE,
AREA_STATUS,
a.last_reason_id,
(Select B.REASON_description
FROM BUSN_REASONS B
WHERE A.LAST_REASON_ID=B.REASON_ID),
a.first_reason_id,
(Select B.REASON_description
FROM BUSN_REASONS B
WHERE A.FIRST_REASON_ID = B.REASON_ID)
FROM BUSN_INFO A
More conventionally, though, you'd just join to the busn_reasons table twice
SELECT i.busn_id,
i.area_name,
i.date,
i.area_status,
i.last_reason_id,
last_reason.reason_description,
i.first_reason_id,
first_reason.reason_description
FROM busn_info i
JOIN busn_reason first_reason
ON( i.first_reason_id = first_reason.reason_id )
JOIN busn_reason last_reason
ON( i.last_reason_id = last_reason.reason_id )

SELECT Statement in CASE

Please don't downgrade this as it is bit complex for me to explain. I'm working on data migration so some of the structures look weird because it was designed by someone like that.
For ex, I have a table Person with PersonID and PersonName as columns. I have duplicates in the table.
I have Details table where I have PersonName stored in a column. This PersonName may or may not exist in the Person table. I need to retrieve PersonID from the matching records otherwise put some hardcode value in PersonID.
I can't write below query because PersonName is duplicated in Person Table, this join doubles the rows if there is a matching record due to join.
SELECT d.Fields, PersonID
FROM Details d
JOIN Person p ON d.PersonName = p.PersonName
The below query works but I don't know how to replace "NULL" with some value I want in place of NULL
SELECT d.Fields, (SELECT TOP 1 PersonID FROM Person where PersonName = d.PersonName )
FROM Details d
So, there are some PersonNames in the Details table which are not existent in Person table. How do I write CASE WHEN in this case?
I tried below but it didn't work
SELECT d.Fields,
CASE WHEN (SELECT TOP 1 PersonID
FROM Person
WHERE PersonName = d.PersonName) = null
THEN 123
ELSE (SELECT TOP 1 PersonID
FROM Person
WHERE PersonName = d.PersonName) END Name
FROM Details d
This query is still showing the same output as 2nd query. Please advise me on this. Let me know, if I'm unclear anywhere. Thanks
well.. I figured I can put ISNULL on top of SELECT to make it work.
SELECT d.Fields,
ISNULL(SELECT TOP 1 p.PersonID
FROM Person p where p.PersonName = d.PersonName, 124) id
FROM Details d
A simple left outer join to pull back all persons with an optional match on the details table should work with a case statement to get your desired result.
SELECT
*
FROM
(
SELECT
Instance=ROW_NUMBER() OVER (PARTITION BY PersonName),
PersonID=CASE WHEN d.PersonName IS NULL THEN 'XXXX' ELSE p.PersonID END,
d.Fields
FROM
Person p
LEFT OUTER JOIN Details d on d.PersonName=p.PersonName
)AS X
WHERE
Instance=1
Ooh goody, a chance to use two LEFT JOINs. The first will list the IDs where they exist, and insert a default otherwise; the second will eliminate the duplicates.
SELECT d.Fields, ISNULL(p1.PersonID, 123)
FROM Details d
LEFT JOIN Person p1 ON d.PersonName = p1.PersonName
LEFT JOIN Person p2 ON p2.PersonName = p1.PersonName
AND p2.PersonID < p1.PersonID
WHERE p2.PersonID IS NULL
You could use common table expressions to build up the missing datasets, i.e. your complete Person table, then join that to your Detail table as follows;
declare #n int;
-- set your default PersonID here;
set #n = 123;
-- Make sure previous SQL statement is terminated with semilcolon for with clause to parse successfully.
-- First build our unique list of names from table Detail.
with cteUniqueDetailPerson
(
[PersonName]
)
as
(
select distinct [PersonName]
from [Details]
)
-- Second get unique Person entries and record the most recent PersonID value as the active Person.
, cteUniquePersonPerson
(
[PersonID]
, [PersonName]
)
as
(
select
max([PersonID]) -- if you wanted the original Person record instead of the last, change this to min.
, [PersonName]
from [Person]
group by [PersonName]
)
-- Third join unique datasets to get the PersonID when there is a match, otherwise use our default id #n.
-- NB, this would also include records when a Person exists with no Detail rows (they are filtered out with the final inner join)
, cteSudoPerson
(
[PersonID]
, [PersonName]
)
as
(
select
coalesce(upp.[PersonID],#n) as [PersonID]
coalesce(upp.[PersonName],udp.[PersonName]) as [PersonName]
from cteUniquePersonPerson upp
full outer join cteUniqueDetailPerson udp
on udp.[PersonName] = p.[PersonName]
)
-- Fourth, join detail to the sudo person table that includes either the original ID or our default ID.
select
d.[Fields]
, sp.[PersonID]
from [Details] d
inner join cteSudoPerson sp
on sp.[PersonName] = d.[PersonName];

List Data in table based on a condition of a column within the table

I have table customers which contains phone number and phone number type. The phone number type includes H, W, M which are home, work and mobile. What I need is to list records showing phone number based on phone type. Example
If customer1 has two phone records, I would like to get a list as following:
customer PhoneH phoneW PhoneM
ABC 123 None 232
Below are the columns in Cust_tbl:
CustomerId, CustomerName, Phone, PhoneType
SQL Fiddle: http://sqlfiddle.com/#!3/d41d8/41007
declare #cust_tbl table
(
CustomerId bigint not null
,CustomerName nvarchar(16) not null
,Phone nvarchar(32) not null
,PhoneType nchar(1) check(PhoneType in ('H','M','W'))
)
insert #cust_tbl
select 1, 'ABC', '123','H'
union select 1, 'ABC', '232','M'
union select 2, 'XXX', '987','W'
select CustomerName Customer
, coalesce([H],'none') PhoneH
, coalesce([W],'none') PhoneW
, coalesce([M],'none') PhoneM
from #cust_tbl
pivot (
max(Phone) for phonetype in ([H],[W],[M])
) pvt
order by Customer
There are different solutions to this depending on the database. For example Pivoting in Access is different then let's say Oracle. Also there are some missing requirements
What happens if a new phone type is introduced should the query add another column?
Should a customer has no phone numbers be suppressed or shown?
If a customer has more than one phone number of a given type what's supposed to happen?
What should happen if the customer name differs for identical customerId's (so much for 2NF)
That said this will produce the output given on what we know from the question on most databases.
It creates a "unique" list of customers with an inline view c . And then joins back 3 times to the the cust_tbl once for each type.
SELECT C.customername customer,
h.phone AS PhoneH,
w.phone AS phoneW,
m.phone AS PhoneM
FROM (SELECT DISTINCT customerid,
customername
FROM cust_tbl) AS c
LEFT JOIN cust_tbl h
ON c.customerid = h.customerid
AND h.phonetype = 'H'
LEFT JOIN cust_tbl w
ON c.customerid = w.customerid
AND w.phonetype = 'W'
LEFT JOIN cust_tbl m
ON c.customerid = m.customerid
AND m.phonetype = 'M'
Demo
This won't address #1, is vague on #2 and is kinda of messy with regards to #3 and #4
To see what I mean by messy see what happens when you get one bad row here

query to get equal number of rows per category + sub category

This is how my SQL table structure looks like:
CREATE TABLE TempCategory
(
Id BIGINT,
Name VARCHAR(100)
)
CREATE TABLE TempSubCategory
(
Id BIGINT,
CategoryId BIGINT,
Name VARCHAR(100)
)
CREATE TABLE TempProduct
(
Id BIGINT,
SubCategoryId BIGINT,
Name VARCHAR(100)
)
http://sqlfiddle.com/#!3/2606fd/4
I am writing a SSRS report that displays Category as x axis, Sub Category as y axis and Products as data. Each Category will have its own Sub Categories so i am displaying Sub Categories in the column group for each row group.
SSRS report does not draw the cells for the rows for which it does not get the values. So my report ends up looking like this:
This is how my current query looks like:
SELECT TempCategory.Id, 'MainCategoryId',
TempCategory.Name 'CategoryName',
TempSubCategory.id 'SubCategoryId',
TempSubCategory.Name 'SubCategory',
TempProduct.Id 'ProductId',
TempProduct.Name 'ProductName'
FROM TempCategory
INNER JOIN TempSubCategory
ON TempCategory.Id = TempSubCategory.CategoryId
INNER JOIN TempProduct
ON TempSubCategory.Id = TempProduct.SubCategoryId
What i am looking to do is modify the query in such a way that it always returns the same number of rows per sub category group so that i have nulls or 0 in the rows for which it does have the data.
For example: Category 1 has 3 sub categories and the max number of products are in Sub Category 1 so i want the query to return 5 (max number of products for sub category 1)
rows for each sub category which has Main Category 1.
For Category 2, it will return 2 rows per Sub Category as max number of products are with Sub Category 2.
Is it possible to do it in SQL or is there some other way to do it in SSRS report?
--Update--
This is a table with ProductName row group
This is matrix with SubCategory column group
This is a table with Product Name row group
Right, here's one (roundabout) way of doing it:
with numbers as -- create lazy numbers table; feel free to replace with a proper one
(
select distinct number
from master..spt_values
where number between 1 and 100
)
, rowCounts as
(
select Category = tc.Name
, SubCategory = tsc.Name
, SubCategoryId = tsc.Id
, MaxSubCatRows = count(1)
from TempCategory tc
inner join TempSubCategory tsc on tc.Id = tsc.CategoryId
inner join TempProduct p on tsc.Id = p.SubCategoryId
group by tc.Name
, tsc.Name
, tsc.Id
)
, maxRowCountPerGroup as
(
select Category
, MaxSubCatRows = max(MaxSubCatRows)
from rowCounts
group by Category
)
, allCats as
(
select rc.Category
, rc.SubCategory
, rc.SubCategoryId
, n.number
from rowCounts rc
inner join maxRowCountPerGroup mr on rc.Category = mr.Category
cross apply (select number
from numbers
where number between 1 and mr.MaxSubCatRows) n
)
, orderedProducts as
(
select *
, productRowNumber = row_number() over (partition by SubCategoryId
order by Id)
from TempProduct
)
select c.Category
, c.SubCategory
, Product = p.Name
from allCats c
left join orderedProducts p on c.subCategoryId = p.subCategoryId
and c.number = p.productRowNumber
order by c.Category
, c.SubCategory
, case when p.Name is null then 1 else 0 end -- nulls last
, p.Name
SQL Fiddle with demo.
So... What this is doing is:
Get row counts for each category/subcategory combo
Get max of these row counts per group
Use numbers table to create n placeholder rows for each
category/subcategory combo, where n is the max for the category
from above
Assign row numbers for each product in a subcategory
Left join our category placeholder rows to our ordered products
Now we have the required number of rows, including the required NULL rows to pad out the SSRS report.
All that is left is to apply this to the report Dataset; sit back and admire the extra rows that have appeared.

Get latest record from second table left joined to first table

I have a candidate table say candidates having only id field and i left joined profiles table to it. Table profiles has 2 fields namely, candidate_id & name.
e.g. Table candidates:
id
----
1
2
and Table profiles:
candidate_id name
----------------------------
1 Foobar
1 Foobar2
2 Foobar3
i want the latest name of a candidate in a single query which is given below:
SELECT C.id, P.name
FROM candidates C
LEFT JOIN profiles P ON P.candidate_id = C.id
GROUP BY C.id
ORDER BY P.name;
But this query returns:
1 Foobar
2 Foobar3
...Instead of:
1 Foobar2
2 Foobar3
The problem is that your PROFILES table doesn't provide a reliable means of figuring out what the latest name value is. There are two options for the PROFILES table:
Add a datetime column IE: created_date
Define an auto_increment column
The first option is the best - it's explicit, meaning the use of the column is absolutely obvious, and handles backdated entries better.
ALTER TABLE PROFILES ADD COLUMN created_date DATETIME
If you want the value to default to the current date & time when inserting a record if no value is provided, tack the following on to the end:
DEFAULT CURRENT_TIMESTAMP
With that in place, you'd use the following to get your desired result:
SELECT c.id,
p.name
FROM CANDIDATES c
LEFT JOIN PROFILES p ON p.candidate_id = c.id
JOIN (SELECT x.candidate_id,
MAX(x.created_date) AS max_date
FROM PROFILES x
GROUP BY x.candidate_id) y ON y.candidate_id = p.candidate_id
AND y.max_date = p.created_date
GROUP BY c.id
ORDER BY p.name
Use a subquery:
SELECT C.id, (SELECT P.name FROM profiles P WHERE P.candidate_id = C.id ORDER BY P.name LIMIT 1);