Query Results from Two Different Tables - sql

I'm trying to write a query to produce a dataset from two or more tables, and I'm having trouble writing the query. I apologize in advanced my lack of knowledge in SQL.
Table 1 consists of basic customer account info and Table 2 consists of customer contract details where one customer account can have multiple contracts, both inactive and active
Table 1 and Table 2 can be joined with the values contained under a column named acct_id.
I would like the query to show only acct_ids where account status (acct_status) is "active" from Table 1, and that do not have an "active" contract from Table 2.
The problem is that in Table 2, there are more than one contract associated to an acct_id and are in different statuses.
If my where clause just focuses on the contract status values from table 2, my dataset won't be accurate. It will only return acct_ids that have contracts with those values.
for example:
acct_iD 123 has 6 contracts: 1 active contract, 4 cancelled contracts, 1 cancel in progress contract
acct_ID 456 has 3 contracts: 3 cancelled contracts
acct_ID 789 has 4 contracts: 2 active contracts, 2 cancelled contracts
acct_ID 012 has 1 contract: 1 cancelled contract
I would like my query result to show only acct_IDs: 456 and 012 as it truly represents that they do not have "active" contracts
I'm using SQL Management Studio 2008 R2.

select acct_id
from table1
where acct_status = "active" and
acct_id not in (select acct_id from table2 where contract_status = "active")

SELECT A.*
FROM Table1 A
WHERE A.acct_status = 'active'
AND NOT A.acct_id in (SELECT acct_id FROM Table2 WHERE contract_status = 'active')

Avoid the horror of IN and sub-selects by utilising LEFT OUTER JOINs like so:
SELECT A.*
FROM Table1 A
LEFT OUTER JOIN table2 B
ON b.acct_id = A.acct_id
AND B.status = 'active'
WHERE b.acct_id IS NULL

IF OBJECT_ID(N'tempdb..#Customer', N'U') is not null drop table #Customer
select
identity(int,1,1) as Customer_ID
, 'John Doe, the Ranger' as name
, '012' as Acct_ID
, 1 as Active
into #Customer
insert into #Customer (name, Acct_ID, Active) values ('Kermit the Frog', '789',1)
select * from #Customer
GO
IF OBJECT_ID(N'tempdb..#Contracts', N'U') is not null drop table #Contracts
select
identity(int,1,1) as Contract_ID
, 1 as Customer_ID
, '012' as Acct_ID
, 123.45 as amt
, 1 as Active
into #Contracts
insert into #Contracts (Customer_ID, Acct_ID, amt, active) values (1, '012', 234.56, 1)
insert into #Contracts (Customer_ID, Acct_ID, amt, active) values (2, '788', 9.56,1)
insert into #Contracts (Customer_ID, Acct_ID, amt, active) values (1, '789', 111.56, 0)
select * from #Contracts A
select *
from #Customer A
where a.Active=1
and (a.Acct_ID not in (select Acct_ID from #Contracts where Active=1))

Related

How can I check if Table B matches quantity of items that exists in Table A? SQL Server

I have 2 tables, table A and Table B, and I want to check if Table B matches quantity of items that exists on Table A, so, for example:
Table A
S_O
ITEM
QTY
1
ITA
1
3
ITB
2
4
ITC
3
6
ITD
0
Table B
S_O
ITEM
QTY
1
ITA
1
3
ITB
2
4
ITC
3
6
ITD
5
7
ITE
2
8
ITF
1
My first thought was to use an except between the two tables, but then I was asked to check if the quantity was OK or if it was shortage to generate a preview like:
Result from comparing the two tables
S_O
ITEM
STATUS
1
ITA
OK
3
ITB
OK
4
ITC
OK
6
ITD
SHORTAGE
And it needs to ignore items "ITE" and "ITF" because they don't exist in Table A
I'm pretty new with sql server queries, I think I could use a SELECT CASE but I don't know how to do it, I'd appreciate some help in this matter
In those tables my unique identifier is S_O, so it would need to match the S_O, item and quantity for both tables
Here's how you can "pre summarise" table a and b to make item unique, then join:
select
A.item,
A.qty as qtya,
B.qty as qtyb,
A.qty - B.qty as shortageamt,
case
when A.qty = B.qty then 'OK'
else 'Shortage'
end as status
from
(
select item, sum(qty) as qty
from tablea
group by item
) as A
inner join
(
select item, sum(qty) as qty
from tableb
group by item
) as B
on A.item = B.item
You'll only get an item listed in the result if it's in both tables - is that what you want? if ITG is in tablea but not tableb, do you want to see it (with qty 0)? That requires an outer join.
I think you should go with a LEFT JOIN. Also, the first answer does not JOIN on S_O, which you state in your question you need.
"In those tables my unique identifier is S_O, so it would need to match the S_O, item and quantity for both tables"
DECLARE #ta TABLE (S_O INT, ITEM VARCHAR(20), QTY INT)
INSERT INTO #ta
VALUES
(1, 'ITA', 1),
(3, 'ITB', 2),
(4, 'ITC', 3),
(6, 'ITD', 0)
DECLARE #tb TABLE (S_O INT, ITEM VARCHAR(20), QTY INT)
INSERT INTO #tb
VALUES
(1, 'ITA', 1),
(3, 'ITB', 2),
(4, 'ITC', 3),
(6, 'ITD', 5),
(7, 'ITE', 2),
(8, 'ITF', 1);
SELECT ta.S_O,
ta.ITEM,
CASE WHEN ta.ITEM = tb.ITEM AND SUM(ta.QTY) >= SUM(tb.QTY) THEN 'OK' ELSE 'SHORTAGE' END AS 'STATUS'
FROM #ta ta
LEFT JOIN #tb tb ON ta.S_O = tb.S_O
GROUP BY ta.S_O, ta.ITEM, tb.ITEM
S_O
ITEM
STATUS
1
ITA
OK
3
ITB
OK
4
ITC
OK
6
ITD
SHORTAGE

Find rows which have never satistifed a condition

Say I have a table of customers with three possible statuses: loan default, open loan, paid in full.
How can I find the customers who never defaulted?
Example: John and Alex had multiple loans with different statuses.
id | customer | status
----------------------
1 john default
1 john open
1 john paid
2 alex open
2 alex paid
John defaulted once and Alex never defaulted. A simple where status <> "default" attempt doesn't work because it incorrectly includes John's non-defaulted cases. The result should give me:
id | customer
-------------
2 alex
How can I find the customers who never defaulted?
You can use aggregation and having:
select id, customer
from t
group by id, customer
having sum(case when status = 'default' then 1 else 0 end) = 0;
The having clause counts the number of defaults for each customer and returns those customers with no defaults.
If you have a separate table of customers, I would recommend not exists:
select c.*
from customers c
where not exists (select 1
from t
where t.id = c.id and t.status = 'default'
);
Something like
select distinct `customer` from `customers`
where `customer` not in (
select `customer` from `customers where `status` = 'default'
);
The ALL() operator with a correlated sub-query works here:
WITH cte AS (
SELECT * FROM (VALUES
(1, 'john', 'default'),
(1, 'john', 'open'),
(1, 'john', 'paid'),
(2, 'alex', 'open'),
(2, 'alex', 'paid')
) AS x(id, customer, status)
)
SELECT *
FROM cte AS a
WHERE 'default' <> ALL (
SELECT status
FROM cte AS b
WHERE a.id = b.id
);
If you want just user and/or id, do select distinct «your desired columns» instead of select *.

SQL QUERY : multiple records against one column value . need to compare another column value to fetch one record

A sample table us like
STATUS INVOICE
=======================
processed 100
reconciled 100
reconciled 200
paid 300
paid 100
paid 200
Output should be
STATUS INVOICE
=======================
processed 100
reconciled 200
paid 300
Logic : If there are multiple statuses against an invoice number , then we should follow the below order to fetch .
Processed > reconciled > paid
Please help me with the SQL query statement for this requirement .
This is a prioritization query. You can handle it using row_number():
select t.*
from (select t.*,
row_number() over (partition by invoice
order by case status when 'Processed' then 1 when 'reconciled' then 2 when 'paid' then 3 else 4 end
) as seqnum
from t
) t
where seqnum = 1;
You need conditional ordering with row_number() :
select top (1) with ties t.*
from table t
order by row_number() over (partition by invoice
order by (case status
when 'Processed'
then 1
when 'reconciled'
then 2
when 'paid'
then 3
else 4
end)
);
Others answers are ok but I'm posting it for the cases there's hundres (or more) of categories because in this case populating a table variable is better than writing lots and lots of CASE statements.
The trick here is to populate a table variable or temp table with your ordering rules and just aggregate by it.
create table dbo.[SAMPLE]
(
[STATUS] varchar(30) not null
,[INVOICE] int not null
)
GO
insert into dbo.[SAMPLE]
values
('processed', 100)
,('reconciled', 100)
,('reconciled', 200)
,('paid', 300)
,('paid', 100)
,('paid', 200)
GO
The below is a partial result showing how it get grouped by your ordering rules
--Processed > reconciled > paid
declare #Ordering as table
(
[STATUS] varchar(30) not null
,[Order] smallint not null
)
insert into #Ordering
values
('processed', 3)
,('reconciled',2)
,('paid',1)
select sp.[INVOICE], max(ord.[Order]) as Precedence
from dbo.[SAMPLE] sp
join #Ordering ord on ord.[STATUS] = sp.[STATUS]
group by sp.[INVOICE]
and below the final query with the expected results
--Processed > reconciled > paid
declare #Ordering as table
(
[STATUS] varchar(30) not null
,[Order] smallint not null
)
insert into #Ordering
values
('processed', 3)
,('reconciled',2)
,('paid',1)
select ord.[STATUS], grouped.INVOICE
from
(
select sp.[INVOICE], max(ord.[Order]) as Precedence
from dbo.[SAMPLE] sp
join #Ordering ord on ord.[STATUS] = sp.[STATUS]
group by sp.[INVOICE]
) as grouped
join #Ordering ord on ord.[Order] = grouped.Precedence
It can be also a interesting solution from performance perspective (acid test required of course).
if you have a status table and the order of status like this
id desc
1 processed
2 reconcilied
3 paid
the better way is joining with this tatble, group by invoice and select max(id)
select i.invoice, max(s.id)
from status s left outer join invoice i
on s.desc = i.status
group by i.invoice
if you havn't this table you can use with to create a virtual table and do this or you can use the case then
https://modern-sql.com/feature/with
https://learn.microsoft.com/it-it/sql/t-sql/language-elements/case-transact-sql?view=sql-server-2017
You can try this.
DECLARE #SampleDate TABLE (STATUS VARCHAR(20), INVOICE INT)
INSERT INTO #SampleDate VALUES
('processed', 100),
('reconciled', 100),
('reconciled', 200),
('paid', 300),
('paid', 100),
('paid', 200)
SELECT STATUS, INVOICE FROM (
SELECT T.*, ROW_NUMBER() OVER(PARTITION BY INVOICE ORDER BY St.ID) AS RN FROM #SampleDate T
INNER JOIN (VALUES (1,'processed'), (2,'reconciled'), (3,'paid')) St(Id, Name) ON T.STATUS = St.Name
) AS X WHERE RN= 1
Result:
STATUS INVOICE
-------------------- -----------
processed 100
reconciled 200
paid 300
WITH TempData As (SELECT MAX(INVOICE) AS INVOICE, [STATUS], CASE WHEN [STATUS] = 'processed' THEN 1 WHEN [STATUS] = 'reconciled' THEN 2 WHEN [STATUS] = 'paid' THEN 3 ELSE 4 END AS SEQ
FROM SAMPLETEST GROUP BY [STATUS])
SELECT [STATUS], INVOICE FROM TempData ORDER BY TempData.SEQ;

SQL Select with Priority

I need to select top 1 most valid discount for a given FriendId.
I have the following tables:
DiscountTable - describes different discount types
DiscountId, Percent, Type, Rank
1 , 20 , Friend, 2
2 , 10 , Overwrite, 1
Then I have another two tables (both list FriendIds)
Friends
101
102
103
Overwrites
101
105
I have to select top 1 most valid discount for a given FriendId. So for the above data this would be sample output
Id = 101 => gets "Overwrite" discount (higher rank)
Id = 102 => gets "Friend" discount (only in friends table)
Id = 103 => gets "Friend" discount (only in friends table)
Id = 105 => gets "Overwrite" discount
Id = 106 => gets NO discount as it does not exist in neither Friend and overwrite tables
INPUT => SINGLE friendId (int).
OUTPUT => Single DISCOUNT Record (DiscountId, Percent, Type)
Overwrites and Friend tables are the same. They only hold list of Ids (single column)
Having multiple tables of identical structure is usually bad practice, a single table with ID and Type would suffice, you could then use it in a JOIN to your DiscountTable:
;WITH cte AS (SELECT ID,[Type] = 'Friend'
FROM Friends
UNION ALL
SELECT ID,[Type] = 'Overwrite'
FROM Overwrites
)
SELECT TOP 1 a.[Type]
FROM cte a
JOIN DiscountTable DT
ON a.[Type] = DT.[Type]
WHERE ID = '105'
ORDER BY [Rank]
Note, non-existent ID values will not return.
This will get you all the FriendIds and the associate discount of the highest rank. It's an older hack that doesn't require using top or row numbering.
select
elig.FriendId,
min(Rank * 10000 + DiscountId) % 10000 as DiscountId
min(Rank * 10000 + Percent) % 10000 as Percent,
from
DiscountTable as dt
inner join (
select FriendId, 'Friend' as Type from Friends union all
select FriendId, 'Overwrite' from Overwrites
) as elig /* for eligible? */
on elig.Type = dt.Type
group by
elig.FriendId
create table discounts (id int, percent1 int, type1 varchar(12), rank1 int)
insert into discounts
values (1 , 20 , 'Friend', 2),
(2 , 10 , 'Overwrite', 1)
create table friends (friendid int)
insert into friends values (101),(102), (103)
create table overwrites (overwriteid int)
insert into overwrites values (101),(105)
select ids, isnull(percent1,0) as discount from (
select case when friendid IS null and overwriteid is null then 'no discount'
when friendid is null and overwriteid is not null then 'overwrite'
when friendid is not null and overwriteid is null then 'friend'
when friendid is not null and overwriteid is not null then (select top 1 TYPE1 from discounts order by rank1 desc)
else '' end category
,ids
from tcase left outer join friends
on tcase.ids = friends.friendid
left join overwrites
on tcase.ids = overwrites.overwriteid
) category1 left join discounts
on category1.category=discounts.type1

Find records that belong to the same identifier, check for multiple occurences of value in column

I have a table which links customer ID's to a sale ID. Multiple customers can be linked the same sale ID, however the first customer should be the Main customer with Type 'M'. All other customers should be type Other ('O').
Cust_ID Sale_ID Cust_Type
1 123 'M'
2 123 'O'
3 124 'M'
4 125 'M'
5 125 'O'
6 125 'O'
Sometimes multiple customers linked to the same Sale ID will be the Main ('M') customer - which is not correct:
Cust_ID Sale_ID Cust_Type
1 123 'M'
2 123 'M'
3 123 'O'
What I wish to be able to do is return a list of Customer ID's, Sale IDs and Customer Types where more than one of the customers in a sale ID are a main customer. I.e. Main ('M') occurs more than once across rows that have the same sale ID.
Any help is greatly appreciated!
So, the problem is that a sales_id can have more than one M value and you want to detect this. I would approach this by using a window function to count those values:
select t.*
from (select t.*,
sum(case when cust_type = 'M' then 1 else 0 end) over (partition by sales_id) as NumMs
from table t
) t
where NumMs > 1;
Actually, I would use the condition NumMs <> 1, because missing the main customer might also be important.
Is this what you mean? This can be achieved using a window function.
CREATE TABLE temp(
Cust_ID INT,
Sale_ID INT,
Cust_Type VARCHAR(1)
)
INSERT INTO temp VALUES
(1, 123, 'M'),
(2, 123, 'M'),
(3, 124, 'M'),
(4, 125, 'M'),
(5, 125, 'O'),
(6, 125, 'O');
WITH CTE AS(
SELECT *, cc = COUNT(*) OVER(PARTITION BY Sale_ID)
FROM temp
WHERE Cust_Type = 'M'
)
SELECT
Cust_ID,
Sale_ID,
Cust_Type
FROM CTE
WHERE cc > 1
DROP TABLE temp
How about this:
SELECT s.Cust_ID, s.Sale_ID, s.Cust_Type
FROM StackOverflow s INNER JOIN
(SELECT Sale_ID
FROM StackOverflow
WHERE Cust_Type = 'M'
GROUP BY Sale_ID
HAVING COUNT(*) > 1) as Multiples ON s.Sale_ID = Multiples.Sale_ID