Sql Query Modification - sql

I have a table called Customer with following schema.
Create Table Customer(id Number,customer_type varchar(20),customer_status char(1),account_number varchar(20));
Insert into Customer(id,customer_type,customer_status,account_number)values(123,'RETAIL','A','32456798');
Insert into Customer(id,customer_type,customer_status,account_number)values(123,'RETAIL','I','92456798');
Insert into Customer(id,customer_type,customer_status,account_number)values(123,'RETAIL','P','22456798');
Insert into Customer(id,customer_type,customer_status,account_number)values(123,'PERSONAL','A','42456798');
Insert into Customer(id,customer_type,customer_status,account_number)values(123,'PERSONAL','I','52456798');
Insert into Customer(id,customer_type,customer_status,account_number)values(123,'PERSONAL','P','62456798');
Insert into Customer(id,customer_type,customer_status,account_number)values(243,'PERSONAL','A','02456798');
commit;
I am trying get Id where customer status is active.Customer_type can be of two types RETAIL or PERSONAL.I just want return Retail true if id have any active reatils accounts else false,
same with Personal
I tried below query but i have trouble returning id
select REATIL,PERSONAL from (select case when customer_status = 'A' then 'Y' else 'N' end as REATIL from Customer where customer_status='A' and customer_type='RETAIL')
,(select id, case when customer_status = 'A' then 'Y' else 'N' end as PERSONAL from Customer where customer_status='A' and customer_type='PERSONAL');
Expected Output:
|---------------------|------------------|----------------|
| id | Retail | Personal |
|---------------------|------------------|----------------|
| 123 | Y | Y |
|---------------------|------------------|----------------|
| 243 | N | Y |
|---------------------|------------------|----------------|
Ay help would be appreciated.

Try the following (make modifications as per your further requirements). I did this as per given data but I feel that the data is not broad enough to test the code. And I made some assumptions as putting blank when there is no match.
Basically to determine whether Retail is Y or N, I used a case statement and did the same for Personal
-- This solution didn't fit as the OP needs 1 record per ID
select ID,
Case When customer_type = 'RETAIL' and customer_status = 'A' then 'Y'
When customer_type = 'RETAIL' and customer_status != 'A' then 'N'
Else ''
End as Retail,
Case When customer_type = 'PERSONAL' and customer_status = 'A' then 'Y'
When customer_type = 'PERSONAL' and customer_status != 'A' then 'N'
Else ''
End as PERSONAL,
account_number
from Customer
Here is the required solution
Select ID, Max(RETAIL) as RETAIL, Max(PERSONAL) as PERSONAL
from
(
select ID,
Case When customer_type = 'RETAIL' and customer_status = 'A' then 'Y'
When customer_type = 'RETAIL' and customer_status != 'A' then 'N'
Else ''
End as Retail,
Case When customer_type = 'PERSONAL' and customer_status = 'A' then 'Y'
When customer_type = 'PERSONAL' and customer_status != 'A' then 'N'
Else ''
End as PERSONAL
from Customer
) Q
Group by ID

try using a join.
the following query returns the account number instead of 'Y'
select C.id, max(R.account_number), max(P.account_number) from Customer C
left join Customer R on R.id = c.id
left join Customer P on P.id = c.id
where R.customer_type = 'RETAIL'
and R.customer_status = 'A'
and P.customer_type = 'PERSONAL'
and P.customer_status = 'A'
group by C.id

Related

SQL select statement that returns records where a set of events have occurred more than once on the same ID

Say I have a table:
CREATE TABLE births
(
childid INT,
momid INT,
eclampsia VARCHAR(1),
preeclampsia VARCHAR(1),
hypertension VARCHAR(1)
);
Insert records:
INSERT INTO BIRTHS (CHILDID, MOMID, ECLAMPSIA)
VALUES (654321, 123456, 'Y'),
(654321, 123456, 'Y'),
INSERT INTO BIRTHS (CHILDID, MOMID, HYPERTENSION)
VALUES (987652, 465468, 'Y'),
(987987, 465468, 'Y')
INSERT INTO BIRTHS (CHILDID, MOMID)
VALUES (687765, 465468)
INSERT INTO BIRTHS (CHILDID, MOMID, PREECLAMPSIA)
VALUES (649870, 846587, 'Y')
INSERT INTO BIRTHS (CHILDID, MOMID)
VALUES (787463, 846587);
I want to return records for all mothers who have had more than one child and have had one of these three diagnoses in more than one pregnancy.
My expected results are:
child momid eclampsia preeclampsia hypertension
-------------------------------------------------------------
654321 123456 Y
431265 123456 Y
987652 465468 Y
987987 465468 Y
How would I do write this?
I have a sloppy query that does not quite do what I want. It works to some degree, but still gives me records where the momid has had a diagnosis only for one pregnancy.
select distinct
a.*, b.eclampsia, b.preeclampsia, b.hypertension
from
births a
join
births b on a.momid = b.momid
where
a.childid != b.childid
and a.eclampsia = 'y'
and (b.eclampsia = 'y' or b.preeclampsia = 'y' or b.hypertension = 'y')
or a.preeclampsia = 'y'
and (b.preeclampsia = 'y' or b.eclampsia = 'Y' or b.hypertension = 'y')
or a.hypertension = 'y'
and (b.hypertension = 'y' or b.eclampsia = 'y' or b.preeclampsia = 'y')
order by
mapersonid
I would solve your problem with this query:
SELECT * FROM births
WHERE momid IN(
SELECT momid FROM births GROUP BY momid
HAVING COUNT(1) >1 AND
SUM(CASE WHEN eclampsia = 'Y' THEN 1 WHEN preeclampsia = 'Y' THEN 1 WHEN hypertension = 'Y' THEN 1 ELSE 0 END) > 1)
AND (eclampsia = 'Y' OR preeclampsia = 'Y' OR hypertension = 'Y')
Basicly, you filter the momids via grouping and formulate your conditions within the HAVING clause and then using this list of momids to build your desired output.
This is one way of doing it. It counts records in the births table which display one of the symptoms for each mother, using that count > 1 as a condition to display the record, as long as the record also shows one of the conditions:
SELECT childid, momid,
COALESCE(eclampsia, '') AS eclampsia,
COALESCE(preeclampsia, '') AS preeclampsia,
COALESCE(hypertension, '') AS hypertension
FROM births b1
WHERE (SELECT COUNT(*) FROM births b2 WHERE b2.momid = b1.momid AND
(ECLAMPSIA = 'Y' OR PREECLAMPSIA = 'Y' OR HYPERTENSION = 'Y')
GROUP BY momid) > 1 AND
(ECLAMPSIA = 'Y' OR PREECLAMPSIA = 'Y' OR HYPERTENSION = 'Y')
Output
child momid eclampsia preeclampsia hypertension
654321 123456 Y
431265 123456 Y
987652 465468 Y
987987 465468 Y
First get the total complications for each mom using CTE and CASEexpression , then join the CTE with Births table on Momid, then filter the moms who have more than one complication. Something like below -
;WITH BirthCTE as(
Select momid,
SUM(CASE WHEN ECLAMPSIA = 'Y' OR PREECLAMPSIA = 'Y' OR HYPERTENSION = 'Y' THEN 1 ELSE 0 END) As TotalComl
FROM births
GROUP BY momid
)
select b.* from births b
inner join BirthCTE cte on b.momid = cte.momid
Where TotalComl > 1 -- More than one complication
and (ECLAMPSIA = 'Y' OR PREECLAMPSIA = 'Y' OR HYPERTENSION = 'Y') -- atleast one complication
This is the wrong data structure. You want a table of births with no complications. Then you want a table birthComplications with one row per complication, if any.
You can restructure the data on the fly. And then aggregation:
select b.momid
from births b outer apply
(select v.complication
from (values ('eclampsia', b.eclampsia), ('hypertension', b.hypertension), ('preeclampsia', b.preeclampsia)
) v(complication, flag)
where flag = 'y'
)
group by b.momid
having count(*) > 1 and -- more than one pregnancy
count(distinct case when v.complication is not null then b.childid end) > 1;
Actually, you can simplify the logic to moms who have had complications in more than one pregnancy. This looks like:
select b.momid
from births b apply -- only keep pregnancies with complications
(select v.complication
from (values ('eclampsia', b.eclampsia), ('hypertension', b.hypertension), ('preeclampsia', b.preeclampsia)
) v(complication, flag)
where flag = 'y'
)
group by b.momid
having count(distinct b.childid) > 1;
try this
select momid, count(*) as "children",
count(eclampsia) as "eclampsia",
count(preeclampsia) as "preeclampsia",
count(hypertension) as "hypertension"
from births
group by momid
having count(*) > 1 and
(
count(eclampsia) > 1 or
count(preeclampsia) > 1 or
count(hypertension) > 1
);
you will get something like:

Updating column conditional on other columns

My server was crashed, damaging my database. In one of my tables I have an academic_quality column where I store school grades like A, B, C+, D. Somehow, my grades are not distributed through other columns on some rows. I thought I could create a conditional clause and create a new column, academic_quality_new, and update it after checking other columns. I tried the following query but it did not work.
SELECT academic_quality, acceptance_rate, undergrads, setting, environment, degrees_offered,
CASE WHEN (academic_quality OR acceptance_rate OR undergrads OR setting OR environment OR degrees_offered) = 'A' THEN INSERT 'A' INTO academic_quality_new
CASE WHEN (academic_quality OR acceptance_rate OR undergrads OR setting OR environment OR degrees_offered) = 'C' THEN INSERT 'B' INTO academic_quality_new
FROM [school_List_V4]
Any help would be appreciated!
You can try by adding case..when for all available grades.
First you can execute select query and check result
select * ,
case when academic_quality = 'A' OR acceptance_rate = 'A' then 'A'
else case when academic_quality ='B' OR acceptance_rate = 'B' then 'B'
else case when academic_quality = 'C' OR acceptance_rate = 'C' then 'C'
else '--'
end
end
end
from table1
then try this update query, it will update new column. If 2 cases are true then it will take first case, so case on grades should be from highest to lowest.
update table1
set new =
case when academic_quality = 'A' OR acceptance_rate = 'A' then 'A'
else case when academic_quality ='B' OR acceptance_rate = 'B' then 'B'
else case when academic_quality = 'C' OR acceptance_rate = 'C' then 'C'
else '--'
end
end
end
and final updated new column is select * from table1
academic_quality | acceptance_rate | new
A | NULL | A
NULL | B | B
NULL | NULL | --
A | B | A
C | B | B

Subqueries in MSSQL producing NULL values

I am trying to determine my store only accounts revenue from the database, to do this I need to look through all account numbers with revenue against a 'store' description who do NOT appear in a list of accounts with an 'online' description which I have tried todo in the subquery below. The query runs however it just returns NULL values in my store_only_revenue column. Any guidance on what to do from here would be appreciated. Am I approaching the problem in a good way? Or is there a better solution:
SELECT
town,
financial_pd as month,
SUM(CASE WHEN [Descr] = 'online' THEN Net_Revenue ELSE 0 END) as online_revenue,
SUM(CASE WHEN [Descr] = 'store' THEN Net_Revenue ELSE 0 END) as store_revenue,
COUNT(DISTINCT CASE WHEN [Descr] = 'online' THEN Account_Number ELSE NULL END) as online_accounts,
COUNT(DISTINCT CASE WHEN [Descr] = 'store' THEN Account_Number ELSE NULL END) as store_accounts,
(SELECT
SUM(Net_Revenue)
FROM [mydb].[dbo].[mytable]
WHERE
Descr = 'store'
AND Account_Number
NOT IN(
SELECT DISTINCT Account_Number
FROM [mydb].[dbo].[mytable]
WHERE
Descr = 'online')
) as store_only_revenue
FROM [mydb].[dbo].[mytable] as orders
WHERE
Group_name = 'T'
AND NOT
Type_name_1 = 'Electronic'
AND
Account_type <> 1
AND
Total_Value > 0
AND
(Insert_Date BETWEEN '2016-05-30' AND '2016-07-03'
OR
Insert_Date BETWEEN '2015-05-25' AND '2015-06-28')
OR
(Insert_Date BETWEEN '2016-05-30' AND '2016-07-03'
AND
Insert_Date BETWEEN '2015-05-25' AND '2015-06-28')
GROUP BY
town,
financial_pd as period
This expression is suspect:
Account_Number NOT IN (SELECT DISTINCT t.Account_Number
FROM [mydb].[dbo].mytable t
WHERE t.Descr = 'online'
)
Assuming that the syntax problems are typos (missing table name, desc is a reserved word), then this will never return true if even one Account_Number is NULL. One way to fix this is:
Account_Number NOT IN (SELECT t.Account_Number
FROM [mydb].[dbo].mytable t
WHERE t.Desc = 'online' AND t.Account_Number IS NOT NULL
)
I would use NOT EXISTS:
not exists (select 1
from [mydb].[dbo].??? x
where x.Desc = 'online' AND ??.Account_Number = x.Account_Number
)
You need to use proper table aliases for this to work. Either of these solutions may fix your problem.

Using CASE to Mark No If No Results From SELECT Statement

is it possible to print "no" if no result found
SELECT mobileno,
CASE
WHEN region = '1234'
THEN 'Yes'
ELSE 'NO'
END
FROM subscriber
WHERE region = '1234'
and status = 1
and mobileno in (77777,88888)
Currently it only print 1 row like
77777,yes
but i want like following
77777,yes
88888,no
Update: One mobileno like 7777 may belongs from two regions then 7777 will get print with NO and YES in two rows if we remove region condition.
Sample Data
sr.No, Name, mobileno, region, status
1, abc, 77777, 1234, 1
2, xyz, 88888, 1222, 1
3, tyu, 22342, 9898, 1
4, abc, 77777, 8787, 1
Sample OutPut
77777, Yes
88888, No
You can 'create' a table by selecting from dual, and left joining :
SELECT t.dummy_num,
CASE WHEN s.mobileno is null then 'No' else 'Yes' end
FROM (SELECT 77777 as dummy_num from dual
UNION select 88888 from dual) t
LEFT JOIN subscriber s
ON(t.dummy_num = s.mobileno and s.region = '1234' and s.status = 1 )
Edit: you can also do it dynamically like this:
SELECT t.mobileno,
CASE WHEN s.mobileno is null then 'No' else 'Yes' end
FROM (select distinct mobileno from subscriber) t
LEFT JOIN subscriber s
ON(t.mobileno= s.mobileno and s.region = '1234' and s.status = 1 )
WHERE t.mobileno IN(777,888,.....)

counting records on the same table with different values possibly none sql server 2008

I have a inventory table with a condition i.e. new, used, other, and i am query a small set of this data, and there is a possibility that all the record set contains only 1 or all the conditions. I tried using a case statement, but if one of the conditions isn't found nothing for that condition returned, and I need it to return 0
This is what I've tried so far:
select(
case
when new_used = 'N' then 'new'
when new_used = 'U' then 'used'
when new_used = 'O' then 'other'
end
)as conditions,
count(*) as count
from myDB
where something = something
group by(
case
when New_Used = 'N' then 'new'
when New_Used = 'U' then 'used'
when New_Used = 'O' then 'other'
end
)
This returns the data like:
conditions | count
------------------
new 10
used 45
I am trying to get the data to return like the following:
conditions | count
------------------
new | 10
used | 45
other | 0
Thanks in advance
;WITH constants(letter,word) AS
(
SELECT l,w FROM (VALUES('N','new'),('U','used'),('O','other')) AS x(l,w)
)
SELECT
conditions = c.word,
[count] = COUNT(x.new_used)
FROM constants AS c
LEFT OUTER JOIN dbo.myDB AS x
ON c.letter = x.new_used
AND something = something
GROUP BY c.word;
try this -
DECLARE #t TABLE (new_used CHAR(1))
INSERT INTO #t (new_used)
SELECT t = 'N'
UNION ALL
SELECT 'N'
UNION ALL
SELECT 'U'
SELECT conditions, ISNULL(r.cnt, 0) AS [count]
FROM (
VALUES('U', 'used'), ('N', 'new'), ('O', 'other')
) t(c, conditions)
LEFT JOIN (
SELECT new_used, COUNT(1) AS cnt
FROM #t
--WHERE something = something
GROUP BY new_used
) r ON r.new_used = t.c
in output -
new 2
used 1
other 0
You can do it as a cross-tab:
select
sum(case when new_used = 'N' then 1 else 0 end) as N,
sum(case when new_used = 'U' then 1 else 0 end) as U,
sum(case when new_used = 'O' then 1 else 0 end) as Other
from myDB
where something = something