Populate SQL Data - sql

I have following data from SQL
| SR.NO | ATTR-X | ATTR-Z |
---------------------------------------
| 1 | A | a1 |
| 2 | B | a2 |
| 3 | C | a3 |
| 4 | A | a4 |
---------------------------------------
I want this to
| SR | A | B | C | ATTR-Z |
----------------------------------
| 1 | A | - | - | a1 |
| 2 | - | B | - | a2 |
| 3 | - | - | C | a3 |
| 4 | A | - | - | a4 |
----------------------------------
Can we do it in SQL queries itself?

Use a CASE statement to determine what is needed in each column.
SELECT SR_NO, CASE WHEN [Attribute -X] = 'A' THEN A ELSE NULL END AS 'A',
CASE WHEN [Attribute -X] = 'B' THEN B ELSE NULL END AS 'B',
CASE WHEN [Attribute -X] = 'C' THEN C ELSE NULL END AS 'C',
[Attribute -Z] AS 'ATTR-Z'
FROM yourtable

Could you clarify a bit further? Do you have that Data in SQL and want it populated in your HTML form?
Or
Did you mean you just want the exact same data to show up in an SQL Query
If just a simple query can do this
select 1 as SR_NO, 'A' as A,'-' as B , '-' as C, 'a1' as [ATTR-Z]
union all
select 2 as SR_NO, '-' as A,'B' as B , '-' as C, 'a2' as [ATTR-Z]
union all
select 3 as SR_NO, 'A' as A,'-' as B , '-' as C, 'a3' as [ATTR-Z]
union all
select 4 as SR_NO, '-' as A,'-' as B , 'C' as C, 'a4' as [ATTR-Z]
However this isn't really useful for anything other than displaying the table you just showed - if you want it to follow a certain pattern/cases then you'd need to let us know more about what you'd want.

Related

Conditional operations without using SWITCH CASE

I have a couple of complex tables. But their mapping is something like:
TABLE_A:
_________________________________
| LINK_ID | TYPE_ID |
_________________________________
| adfasdnf23454 | TYPE 1 |
| 43fsdfsdcxsh7 | TYPE 1 |
| dfkng037sdfbd | TYPE 1 |
| sd09734fdfhsf | TYPE 2 |
| khsadf94u5dfc | TYPE 2 |
| piukvih66wfa8 | TYPE 3 |
_________________________________
TABLE_B:
_____________________________________________
| LINK_ID | CODE_ID | CODE_VALUE |
_____________________________________________
| adfasdnf23454 | 7 | test 1 |
| fgubk65esdfj7 | 6 | test 2 |
| ooogfsg354fds | 7 | test 3 |
| sd09734fdfhsf | 5 | test 4 |
_____________________________________________
The LINK_ID column links these two tables.
My requirement is to have all the records from TABLE_A checked whether they have a specific CODE_ID or not.
If the record has CODE_ID as 7 - populate CODE_VALUE in a column.
If the record has CODE_ID as 6 - populate CODE_VALUE in another column.
If the record doesn't have a CODE_ID show CODE_VALUE as null.
The catch is, TABLE_B may have records that TABLE_A don't. But the final result should contain the records of TABLE_A alone.
PS: SWITCH CASE not suggested since I would require the fields in the same row. (Please see the multiple rows for same LINK_ID in OBTAINED RESULT on using SWITCH CASE)
OBTAINED RESULT:
_______________________________________________
| LINK_ID | CODE_VALUE_1 | CODE_VALUE_1 |
_______________________________________________
| adfasdnf23454 | test 1 | null |
| adfasdnf23454 | null | test 4 |
| sd09734fdfhsf | test 6 | null |
_______________________________________________
EXPECTED RESULT:
__________________________________________________
| LINK_ID | CODE_VALUE_1 | CODE_VALUE_2 |
__________________________________________________
| adfasdnf23454 | test 1 | test 4 |
| 43fsdfsdcxsh7 | null | null |
| dfkng037sdfbd | null | null |
| sd09734fdfhsf | test 6 | null |
| khsadf94u5dfc | null | null |
| piukvih66wfa8 | null | null |
__________________________________________________
Can someone help on this?
One option uses two correlated subqueries:
select
a.link_id,
(select code_value from table_b b where b.link_id = a.link_id and b.code_id = 7) code_value_1,
(select code_value from table_b b where b.link_id = a.link_id and b.code_id = 6) code_value_2
from table_a a
Note that this assumes no duplicate (link_id, code_id) in table_b. You could also write this with two left joins - which is quite the same logic.
Another solution is a single left join, then conditional aggregation:
select
a.link_id,
max(case when b.code_id = 7 then b.code_value end) code_value_1,
max(case when b.code_id = 6 then b.code_value end) code_value_2
from table_a a
left join table_b b on b.link_id = a.link_id and b.code_id in (6, 7)
group by a.link_id
Problematic part of your question is what to do if two entries in B have same link_id and type_id. You can use min, max, last entry (but for that you need ordering column in B). Or you can list them all:
select *
from a left join b using (link_id)
pivot (listagg(code_value, ', ') within group (order by code_value)
for code_id in (6 as code6, 7 as code7))
Data:
create table a (link_id, type_id) as (
select 'a', 'TYPE 1' from dual union all
select 'b', 'TYPE 1' from dual union all
select 'c', 'TYPE 1' from dual union all
select 'd', 'TYPE 2' from dual );
create table b(LINK_ID, CODE_ID, CODE_VALUE) as (
select 'a', 6, 'test 1' from dual union all
select 'a', 7, 'test 2' from dual union all
select 'a', 7, 'test 3' from dual union all
select 'b', 7, 'test 4' from dual union all
select 'd', 6, 'test 5' from dual );
Result:
LINK_ID TYPE_ID CODE6 CODE7
a TYPE 1 test 1 test 2, test 3
b TYPE 1 test 4
c TYPE 1
d TYPE 2 test 5
dbfiddle

SQL select distinct when one column in and another column greater than

Consider the following dataset:
+---------------------+
| ID | NAME | VALUE |
+---------------------+
| 1 | a | 0.2 |
| 1 | b | 8 |
| 1 | c | 3.5 |
| 1 | d | 2.2 |
| 2 | b | 4 |
| 2 | c | 0.5 |
| 2 | d | 6 |
| 3 | a | 2 |
| 3 | b | 4 |
| 3 | c | 3.6 |
| 3 | d | 0.2 |
+---------------------+
I'm tying to develop a sql select statement that returns the top or distinct ID where NAME 'a' and 'b' both exist and both of the corresponding VALUE's are >= '1'. Thus, the desired output would be:
+---------------------+
| ID | NAME | VALUE |
+---------------------+
| 3 | a | 2 |
+----+-------+--------+
Appreciate any assistance anyone can provide.
You can try to use MIN window function and some condition to make it.
SELECT * FROM (
SELECT *,
MIN(CASE WHEN NAME = 'a' THEN [value] end) OVER(PARTITION BY ID) aVal,
MIN(CASE WHEN NAME = 'b' THEN [value] end) OVER(PARTITION BY ID) bVal
FROM T
) t1
WHERE aVal >1 and bVal >1 and aVal = [Value]
sqlfiddle
This seems like a group by and having query:
select id
from t
where name in ('a', 'b')
having count(*) = 2 and
min(value) >= 1;
No subqueries or joins are necessary.
The where clause filters the data to only look at the "a" and "b" records. The count(*) = 2 checks that both exist. If you can have duplicates, then use count(distinct name) = 2.
Then, you want the minimum value to be 1, so that is the final condition.
I am not sure why your desired results have the "a" row, but if you really want it, you can change the select to:
select id, 'a' as name,
max(case when name = 'a' then value end) as value
you can use in and sub-query
select top 1 * from t
where t.id in
(
select id from t
where name in ('a','b')
group by id
having sum(case when value>1 then 1 else 0)>=2
)
order by id

Get ID if table has one or more row exist for a condition

Suppose I have a table as below:
ID | Account| Status
---+--------+-------
1 | acct1 | A
1 | acct2 | S
1 | acct3 | C
2 | acct4 | C
2 | acct5 | C
3 | acct6 | A
3 | acct7 | C
4 | acct8 | C
4 | acct9 | C
4 | acct10 | C
Condition: return ID if accounts do not have any 'A' and 'S' status.
For this case, I only want ID '2' and '4' to be returned.
You could use HAVING and conditional SUM:
SELECT ID
FROM tab
GROUP BY ID
HAVING SUM(CASE WHEN Status IN ('A', 'S') THEN 1 ELSE 0 END) = 0
First select id which record don't have 'A' and 'S'. Then get distinct record:
Select distinct(ID) as ID
from table_name where id not in
(
select ID from table_name where status in('A', 'S')
)

Select query where record count = 2 and column contains either two values

Example 1
+--------------------------+
| IDENT | CURRENT | SOURCE |
+--------------------------+
| 12345 | 12345 | A |
| 23456 | 12345 | B |
| 34567 | 12345 | C |
+--------------------------+
Example 2
+--------------------------+
| IDENT | CURRENT | SOURCE |
+--------------------------+
| 12345 | 55555 | A |
| 23456 | 55555 | B |
+--------------------------+
Trying to write select query that will show all records that CURRENT count = 2 and SOURCE contains both A and B (NOT C).
Example A should not show up as there are 3 entries for the CURRENT as record is linked to SOURCE C.
Example B is what I'm looking the query to find, CURRENT has two records and is only linked to SOURCE 'A' and 'B'.
Currently if I run something similar to "where SOURCE = A or SOURCE = B", results are records that just have SOURCE of A, OR A+C.
NOTES: IDENT is always a unique value. CURRENT links multiple IDENTS from different SOURCE's.
We're clearly missing more information. Let's take example data (thanks gloomy for the initial fiddle).
| ID | CURRENT | SOURCE |
|----|---------|--------|
| 1 | 111 | A |
| 2 | 111 | B |
| 3 | 111 | C |
| 4 | 222 | A |
| 5 | 222 | B |
| 6 | 333 | A |
| 7 | 333 | C |
| 8 | 444 | B |
| 9 | 444 | C |
| 10 | 555 | B |
| 11 | 666 | A |
| 12 | 666 | A |
| 13 | 666 | B |
| 14 | 777 | A |
| 15 | 777 | A |
I assume you only need this as the result:
| ID | CURRENT | SOURCE |
|----|---------|--------|
| 4 | 222 | A |
| 5 | 222 | B |
This query will work with any amount of sources and result in the expected output:
SELECT * FROM test
WHERE CURRENT IN (
SELECT CURRENT FROM test
WHERE CURRENT NOT IN (
SELECT CURRENT FROM test
WHERE SOURCE NOT IN ('A', 'B')
)
GROUP BY CURRENT
HAVING count(SOURCE) = 2 AND count(DISTINCT SOURCE) = 2
)
If SOURCE values are guaranteed to be unique per CURRENT:
SELECT CURRENT
FROM atable
GROUP BY CURRENT
HAVING COUNT(SOURCE) = 2
AND COUNT(CASE WHEN SOURCE IN ('A', 'B') THEN SOURCE END) = 2
;
If SOURCE values aren't unique per CURRENT but CURRENTs with duplicate entries of 'A' or 'B' are allowed:
SELECT CURRENT
FROM atable
GROUP BY CURRENT
HAVING COUNT(DISTINCT SOURCE) = 2
AND COUNT(DISTINCT CASE WHEN SOURCE IN ('A', 'B') THEN SOURCE END) = 2
;
If SOURCE values aren't unique and groups with duplicate SOURCE entries aren't allowed:
SELECT CURRENT
FROM atable
GROUP BY CURRENT
HAVING COUNT(SOURCE) = 2
AND COUNT(DISTINCT SOURCE) = 2
AND COUNT(DISTINCT CASE WHEN SOURCE IN ('A', 'B') THEN SOURCE END) = 2
;
Every query returns only distinct CURRENT values matching the requirements. Use the query as a derived dataset and join it back to your table to get the details.
All the above options assume that either SOURCE is a NOT NULL column or that NULLs can just be disregarded.
Records where current count = 2:
SELECT CURRENT
FROM table
GROUP BY CURRENT
HAVING COUNT(*) = 2
Records where C is in SOURCE values:
SELECT CURRENT
FROM table
WHERE SOURCE = 'C'
Global query:
SELECT t.*
FROM TABLE t
WHERE t.CURRENT IN (
SELECT CURRENT
FROM table
GROUP BY CURRENT
HAVING COUNT(*) = 2
) AND t.CURRENT NOT IN (
SELECT CURRENT
FROM table
WHERE SOURCE = 'C'
)
http://sqlfiddle.com/#!2/69be9/8/0
select * from test where current in (
select test_a.current
from
(select *
from test
where source = 'A') as test_a
join (select *
from test
where source = 'B') as test_b
on test_b.current = test_a.current
where test_a.current not in
(select current from test where source='C')
)
SELECT *
FROM TABLE mainTbl,
(SELECT CURRENT
FROM TABLE
WHERE source IN ('A', 'B')
HAVING COUNT(1) = 2
GROUP BY CURRENT
) selectedSet
WHERE mainTbl.current = selectedSet.current
AND mainTbl.source IN ('A', 'B');

Find list of values in list of values

I'm trying to write a sql with a where clause, that checks if any element in a list is in another list. Is there a shorter way to accomplish this rather than check each member of the first list?
SELECT * from FOO
WHERE FOO.A IN ('2','3', '5', '7','11','13','17','19') OR
FOO.B IN ('2','3', '5', '7','11','13','17','19') OR
FOO.C IN ('2','3', '5', '7','11','13','17','19') OR
FOO.D IN ('2','3', '5', '7','11','13','17','19') OR
FOO.E IN ('2','3', '5', '7','11','13','17','19') OR
FOO.F IN ('2','3', '5', '7','11','13','17','19')
That is the simplified sql.
Was trying not to muddy waters too much, but since you ask:
Ultimately what I am trying to do here is, select rows from FOO, that has columns fulfilling various criteria. These criteria are stored in a second table (call it BAR), mainly db, name, type must match and flag must be 1. Was planning to build the IN list from BAR, comparing them with column names in INFORMATION_SCHEMA.COLUMNS containing FOO
FOO:
+--------+--------+---------+---------+--------+-------+
| DB | Name | Type | Col1 | Col2 | Col3 |
+--------+--------+---------+---------+--------+-------+
| 4 | AC1 | LO | 1 | 10 | 2 |
| 4 | AC1 | HI | 2 | 20 | 4 |
| 1 | DC2 | HI-HI | 11 | 5 | 2 |
| 1 | DC2 | HI | 22 | 10 | 4 |
| 1 | DC2 | LO | 33 | 15 | 6 |
+--------+--------+---------+---------+--------+-------+
BAR:
+--------+--------+---------+---------+--------+
| DB | Name | Type | Field | Flag |
+--------+--------+---------+---------+--------+
| 4 | AC1 | LO | Col1 | 1 |
| 4 | AC1 | HI | Col1 | 1 |
| 1 | DC2 | HI-HI | Col1 | 1 |
| 1 | DC2 | HI | Col1 | 1 |
| 1 | DC2 | LO | Col1 | 1 |
| 4 | AC1 | LO | Col2 | 0 |
| 4 | AC1 | HI | Col2 | 0 |
| 1 | DC2 | LO | Col2 | 0 |
| 1 | DC2 | HI-HI | Col2 | 0 |
| 1 | DC2 | HI | Col2 | 0 |
| 4 | AC1 | LO | Col3 | 0 |
| 4 | AC1 | HI | Col3 | 0 |
| 1 | DC2 | LO | Col3 | 0 |
| 1 | DC2 | HI-HI | Col3 | 0 |
| 1 | DC2 | HI | Col3 | 0 |
+--------+--------+---------+---------+--------+
On first examination, it would seem your schema is not appropriate for the type of query you're performing. It seems like you would want a FOOVAL table with a type and a value then you're query simply becomes:
CREATE TABLE FOOVAL
{
ID int, -- References FOO.ID
TYPE char, -- A, B, C, D, E, F
VAL int
}
SELECT * FROM FOO WHERE FOO.ID IN
(SELECT DISTINCT FOOVAL.ID WHERE FOOVAL.VAL IN ('2','3', '5', '7','11','13','17','19'))
Your method probably performs the best. Here is an alternative that only requires creating the list once. It uses a CTE to create a list of the values and then an exists clause to check whether any values match:
with vals as (
select '2' as p union all
select '3' union all
select '5' union all
select '7' union all
select '11' union all
select '13' union all
select '17' union all
select '19'
)
select *
from foo
where exists (select 1 from vals where vals.p in (foo.A, foo.B, foo.C, foo.D, foo.E, foo.F))
If you are using a database that doesn't support CTEs, you can just put the code in the where clause:
select 8
from foo
where exists (select 1
from (select '2' as p union all
select '3' union all
select '5' union all
select '7' union all
select '11' union all
select '13' union all
select '17' union all
select '19'
) t
where vals.p in (foo.A, foo.B, foo.C, foo.D, foo.E, foo.F)
)
If you are using Oracle, then you need to add from dual in the statements after the string constants. Otherwise, I think one or the other should work in any SQL database.
While it is not exactly clear what you want to do with the data, since you are using SQL Server my suggestion would be to use the UNPIVOT function to turn the col1, col2 and col3 columns into rows which will make it easier to filter the data:
select db, name, type, col, value
from foo
unpivot
(
value
for col in (Col1, Col2, Col3)
) unpiv;
See SQL Fiddle with Demo. This gives the data in the following format:
| DB | NAME | TYPE | COL | VALUE |
------------------------------------
| 4 | AC1 | LO | Col1 | 1 |
| 4 | AC1 | LO | Col2 | 10 |
| 4 | AC1 | LO | Col3 | 2 |
| 4 | AC1 | HI | Col1 | 2 |
Once the is in the row format, it should be significantly easier to apply any filters or even join to your BAR table.