Table (Input data)
+--------+---------+
| col_id | col_val |
+--------+---------+
| axc | col_x |
| bdf | col_f |
| cde | col_x |
| yhc | col_f |
| idx | col_a |
| dft | col_b |
+--------+---------+
Tranpose logic. basically wanted to combine col_a or col_b values into one column as col_ab. tried as below. but running into an error. please help
SELECT a.col_id,IF(array_contains(collect_list(a.map_values['col_x']),'1'),'Y','N') AS col_x,
IF(array_contains(collect_list(a.map_values['col_y']),'1'),'Y','N') AS col_y,
IF(array_contains(collect_list(a.map_values['col_a']),'1'),'Y','N') OR IF(array_contains(collect_list(a.map_values['col_b']),'1'),'Y','N') AS col_ab,
IF(array_contains(collect_list(a.map_values['col_f']),'1'),'Y','N') AS col_f
FROM (
SELECT col_id,
col_val,
map(col_val, '1') map_values
FROM transpose) a GROUP BY a.col_id;
Output Data
+--------+-------+-------+-----------+-------+
| col_id | col_x | col_y | col_ab | col_f |
+--------+-------+-------+-----------+-------+
| axc | Y | N | N | N |
| bdf | N | N | N | Y |
| cde | Y | N | N | N |
| dft | N | Y | Y | N |
| idx | N | N | Y | N |
| yhc | N | N | N | Y |
+--------+-------+-------+-----------+-------+
SELECT col_id,
IF(map_values['col_x'],'Y','N') as col_x,
IF(map_values['col_y'],'Y','N') as col_y,
IF((map_values['col_a'] || map_values['col_b']),'Y','N') as col_ab,
IF(map_values['col_f'],'Y','N') as col_f
FROM
(SELECT col_id,
to_map(col_val, true) map_values
FROM transpose
GROUP BY col_id)a
Use case statements with aggregation:
select col_id,
max( case col_val = 'col_x' then 'Y' alse 'N' end ) as col_x,
max( case col_val = 'col_y' then 'Y' alse 'N' end ) as col_y,
max( case col_val in ( 'col_a', 'col_b') then 'Y' alse 'N' end ) as col_ab,
max( case col_val = 'col_f' then 'Y' alse 'N' end ) as col_f
from your_table
group by col_id;
Related
Hi everyone I am using SQL Server 2016, I have a table called support_event_log that looks like this:
| event_nr | data |
|--------------|-------------|
| 1 | x |
| 2 | x |
And a table called support_event_log_params that looks like this:
| event_nr | msg_param_nr | msg_param_value |
|-----------------|----------------|------------------|
| 1 | 1 | x |
| 2 | 1 | x |
| 2 | 2 | y |
| 2 | 3 | z |
I want to join both tables by the column Event_nr and pivot the column msg_param_nr into 3 different columns depending on the number with the value of the column msg_param_value, like this:
| event_nr | msg1 | msg2 | msg3 | data |
|-----------------|------|------|------| x |
| 1 | x | null | null | x |
| 2 | x | y | z | x |
I first tried the following query:
SELECT A.event_nr
,A.data
,CASE WHEN B.msg_param_nr = 1 THEN B.msg_param_value END AS msg1
,CASE WHEN B.msg_param_nr = 2 THEN B.msg_param_value END AS msg2
,CASE WHEN B.msg_param_nr = 3 THEN B.msg_param_value END AS msg3
FROM support_event_log A LEFT JOIN support_event_log_params B
on A.event_nr=B.event_nr
but I was getting the following result with repeated rows:
| event_nr | msg1 | msg2 | msg3 | data |
|-----------------|------|------|------| x |
| 1 | x | null | null | x |
| 2 | x | null | null | x |
| 2 | null | y | null | x |
| 2 | null | null | z | x |
Finally after a lot of thinking I got a working solution with the following query:
WITH col1 AS (
SELECT A.event_nr, A.msg_param_value
FROM support_event_log_params A
WHERE A.msg_param_nr=1
)
, col2 AS (
SELECT A.event_nr, A.msg_param_value
FROM support_event_log_params A
WHERE A.msg_param_nr=2
)
,col3 AS (
SELECT A.event_nr, A.msg_param_value
FROM support_event_log_params A
WHERE A.msg_param_nr=3
)
SELECT A.event_nr
,A.data
,B.msg_param_value as msg1
,C.msg_param_value as msg2
,D.msg_param_value as msg3
FROM support_event_log A
LEFT JOIN col1 B on A.event_nr=B.event_nr
LEFT JOIN col2 C on A.event_nr=C.event_nr
LEFT JOIN col3 D on A.event_nr=D.event_nr
but it seems very inefficient doing the 3 withs to the same table, is there a better solution to this problem ? I can't seem to find one that works
You just need aggregation on your first query:
SELECT el.event_nr, el.data,
MAX(CASE WHEN elp.msg_param_nr = 1 THEN elp.msg_param_value END) AS msg1,
MAX(CASE WHEN elp.msg_param_nr = 2 THEN elp.msg_param_value END) AS msg2,
MAX(CASE WHEN elp.msg_param_nr = 3 THEN elp.msg_param_value END) AS msg3
FROM support_event_log el LEFT JOIN
support_event_log_params elp
ON el.event_nr = elp.event_nr
GROUP BY el.event_nr, el.data;
Notice that I also changed the table aliases to be abbreviations for the table names, rather than meaningless letters such as A and B.
I've two Table
Table 1
+--------+--------+
| LC | STATUS |
+--------+--------+
| 010051 | 6 |
+--------+--------+
| 010071 | 2 |
+--------+--------+
| 010048 | 2 |
+--------+--------+
| 010113 | 2 |
+--------+--------+
| 010125 | 2 |
+--------+--------+
Table 2
+--------+-------------+-----------+------------+--------+
| LC | BILL | LAST_BILL | PAYMENT_BY | STATUS |
+--------+-------------+-----------+------------+--------+
| 010125 | BILL/17/001 | 0 | C | 6 |
+--------+-------------+-----------+------------+--------+
| 010125 | BILL/17/002 | 0 | I | 1 |
+--------+-------------+-----------+------------+--------+
| 010125 | BILL/17/003 | 0 | F | 1 |
+--------+-------------+-----------+------------+--------+
| 010125 | BILL/17/004 | 0 | C | 6 |
+--------+-------------+-----------+------------+--------+
| 010113 | BILL/17/005 | 0 | C | 6 |
+--------+-------------+-----------+------------+--------+
| 010113 | BILL/17/006 | 0 | I | 1 |
+--------+-------------+-----------+------------+--------+
| 010048 | BILL/17/007 | 0 | C | 6 |
+--------+-------------+-----------+------------+--------+
| 010071 | BILL/17/008 | 0 | C | 6 |
+--------+-------------+-----------+------------+--------+
Where I just want to get the LC whose PAYMENT_BY is 'C', but others who have 'C' value and other than 'C' value, I don't want to get this LC.
I've try following query, but I think there's have expert who can done it in better way or most tuning way.
SELECT LC
FROM (SELECT T1.LC
FROM TABLE1 T1, TABLE2 T2
WHERE T1.STATUS = 2
AND T1.LC = T2.LC
AND T2.PAYMENT_BY = 'C'
AND LAST_BILL = 0
AND T2.STATUS = 6
MINUS
SELECT T1.LC
FROM TABLE1 T1, TABLE2 T2
WHERE T1.STATUS = 2
AND T1.LC = T2.LC
AND T2.PAYMENT_BY = 'I'
AND LAST_BILL = 0)
Query/Expected Result:
+--------+
| LC |
+--------+
| 010048 |
+--------+
| 010071 |
+--------+
You can do it with NOT EXISTS:
select t2.lc from table2 t2
where
t2.payment_by = 'C'
and
not exists (
select lc from table2
where lc = t2.lc and payment_by <> 'C'
)
If you want all the columns of table2, then:
select t2.* from table2 t2
..........................
select t.lc,
count(case when t.payment_by = 'C' THEN 1 else NULL end ) as count_c,
count(case when t.payment_by <> 'C' THEN 1 else NULL end ) as count_not_c
from table2 t
group by t.lc
having count(case when t.payment_by <> 'C' THEN 1 else NULL end ) < 1
demo
If I understand correctly, I think group by and having is the simplest query:
select t2.lc
from table2 t2
group by t2.lc
having min(t2.payment_by) = 'C' and max(t2.payment_by) = 'C';
This also has the advantage of returning each lc exactly once.
Let's say I have this table:
+------+-------+-------+-------+-------+--------+
| User | Value | Rule1 | Rule2 | Rule3 | Rule4 |
+------+-------+-------+-------+-------+--------+
| 1 | 10 | 20 | 14 | 15 | 22 |
| 2 | 5 | 20 | 7 | 8 | 25 |
+------+-------+-------+-------+-------+--------+
I want to do a partition by query, like that:
SELECT sum(Value) OVER (PARTITION BY Rule1, Rule2)
FROM MY_TABLE
but i don't want to write "Rule1, Rule2". I want to read a table like
+----+-------+
| ID | Rules |
+----+-------+
| 1 | Rule1 |
| 1 | Rule2 |
| 2 | Rule1 |
| 2 | Rule2 |
| 2 | Rule3 |
| 3 | Rule2 |
| 3 | Rule3 |
| 3 | Rule4 |
+----+-------+
So i can write something like that:
SELECT sum(Value) OVER (PARTITION BY "all rules separated by comma where ID = 1")
FROM MY_TABLE
Is it possible? Someone has a better alternative?
Thanks in advance!
Is this what you want?
select t.*,
sum(value) over
(partition by (case when exists (select 1 from tablelikethis tlt where tlt.id = 1 and tlt.rules = 'rule1') then t.rule1 end),
(case when exists (select 1 from tablelikethis tlt where tlt.id = 1 and tlt.rules = 'rule2') then t.rule2 end),
(case when exists (select 1 from tablelikethis tlt where tlt.id = 1 and tlt.rules = 'rule3') then t.rule3 end),
(case when exists (select 1 from tablelikethis tlt where tlt.id = 1 and tlt.rules = 'rule4') then t.rule4 end)
)
from my_table t
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.
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.