Oracle Script for target table - sql

Hi I have the following requirement
In Table A
CRD | RNo | J_NAME
-------------------------
DOS1 |1 | NULL
DOS2 |2 | Name 1
DOS3 |3 | Name 2
DOS4 |4 | Name 3
DOS5 |5 | Name 1
DOS6 |6 | Name 1
DOS7 |7 | Name 4
DOS8 |8 | Name 2
Out put should be
CRD | RNo | J_NAME
-------------------------
DOS1 |1 | NULL
DOS2 |2 | A
DOS3 |3 | B
DOS4 |4 | C
DOS5 |5 | A
DOS6 |6 | A
DOS7 |7 | D
DOS8 |8 | B
Null allays should be null, If the name already exist in the target table then It will be add the same name eg: J_Name = A and B, if the source value is not in the target table then it will get a new entry from the list.
Ho I can achieve this?

You can try somthing like this:-
SELECT CRD, RNo,
CASE WHEN J_NAME ='Name 1' THEN 'A'
WHEN J_NAME ='Name 2' THEN 'B'
WHEN J_NAME ='Name 3' THEN 'C'
WHEN J_NAME ='Name 4' THEN 'D'
ELSE 'NULL' END AS J_NAME
FROM TAB;

If you're sure you won't run out of letters (since you're not telling how to generate values beyond Z), you could just dense_rank your names and give them a letter corresponding to the rank;
WITH cte AS (
SELECT * FROM mytable UNION ALL SELECT '', 0, NULL FROM DUAL
)
SELECT crd, rno, CASE WHEN j_name IS NULL THEN NULL ELSE CHR(calc+63) END j_name
FROM (
SELECT crd, rno, j_name, DENSE_RANK() OVER (ORDER BY j_name NULLS FIRST) calc
FROM cte
)
WHERE rno > 0 ORDER BY rno;
It basically takes the table contents, adds a row with a null J_NAME value to make sure there's a rank for NULL, and uses DENSE_RANK() on the resulting j_names to get a value to generate the letter from.
An SQLfiddle to test with.

Related

SQL query to split and keep only the top N values

I have the following table data:
| name |items |
--------------------
| Bob |1, 2, 3 |
| Rick |5, 3, 8, 4|
| Bill |2, 4 |
I need to create a table with a split items column, but with the limitation to have at most N items per name. E.g. for N = 3 the table should look like this:
|name |item|
-----------
|Bob |1 |
|Bob |2 |
|Bob |3 |
|Rick |5 |
|Rick |3 |
|Rick |8 |
|Bill |2 |
|Bill |4 |

I have the following query that splits items correctly, but doesn't account for the maximum number N. What should I modify in the query (standard SQL, BigQuery) to account for N?
WITH data_split AS (
SELECT name, SPLIT(items,',') AS item
FROM (
SELECT name, items
-- A lot of additional logic here
FROM data
)
)
SELECT name, item
FROM data_split
CROSS JOIN UNNEST(data_split.item) AS item
You can try a more semi-standard way - works practically everywhere:
WITH
-- your input ...
indata(id,nam,items) AS ( -- need a sorting column "id" to keep the sort order
SELECT 1, 'Bob' ,'1,2,3' -- blanks after comma can irritate
UNION ALL SELECT 2, 'Rick','5,3,8,4' -- the splitting function below ...
UNION ALL SELECT 3, 'Bill','2,4'
)
-- real query starts here, replace comma below with "WITH" ...
,
-- exactly 3 integers
i(i) AS (
SELECT 1 -- need to add FROM DUAL , in Oracle, for example ...
UNION ALL SELECT 2
UNION ALL SELECT 3
)
SELECT
id
, nam
, SPLIT(items,',',i) AS item -- SPLIT_PART in other DBMS-s
FROM indata CROSS JOIN i
WHERE SPLIT_PART(items,',',i) <> ''
ORDER BY 1, 3
;
-- out id | nam | item
-- out ----+------+------
-- out 1 | Bob | 1
-- out 1 | Bob | 2
-- out 1 | Bob | 3
-- out 2 | Rick | 3
-- out 2 | Rick | 5
-- out 2 | Rick | 8
-- out 3 | Bill | 2
-- out 3 | Bill | 4
Consider below approach (BigQuery)
select name, trim(item) item
from your_table, unnest(split(items)) item with offset
where offset < 3
if applied to sample data in your question - output is

How to make MS SQL select on this

I have MS SQL database table like this
TableA
+----+-----------+--------+
|ID | Table2_FK | Value |
+----+-----------+--------+
|1 | 7 | X |
|2 | 7 | Y |
|3 | 8 | X |
|4 | 8 | Z |
|5 | 9 | W |
|6 | 9 | M |
|5 | 10 | X |
|6 | 10 | Z |
+----+-----------+--------+
I want to make query to get list of Table2_FKs if I pass X and Z in query for Values. In this example 8 and 10 is the result
It can be more than 2 values
You can do this with group by and having:
select table2_fk
from t
where value in ('X', 'Z')
group by table2_fk
having count(*) = 2;
If the values can be duplicated for a key value, then use count(distinct value) = 2. The "2" is the number of values in the IN list.
Try this:
select distinct Table2_FK
from TableA
where value in ('X','Z');
You can use query as below:
Select distinct table2_fk from (
Select *, Ct = count(id) over (partition by table2_fk) from yourtable
) a
Where a.[Value] in ('X','Z') and a.Ct >= 2
you can use a query like below
select
distinct Table2_FK
from TableA a
where exists (
select 1 1 from TableA b where b.value ='X' and a.Table2_FK =b.Table2_FK
)
and exists (
select 1 1 from TableA c where c.value ='Z' and a.Table2_FK =c.Table2_FK
)

On MS Access, group by then filter

I have a table on ms access which has 13 columns.I want to group by column Name then check the latest one by comparing column id and take the record if the latest row has value if not take the previous record. the comparison will be done for each columns.
+-----+-----+-------+-------+-------+
| id |Name |colum1 |colum2 |colum3 |
+-----+-----+-------+-------+-------+
| 1 |a |x | |x |
+-----+-----+-------+-------+-------+
| 2 |b | |y |y |
+-----+-----+-------+-------+-------+
| 3 |a |z |z | |
+-----+-----+-------+-------+-------+
| 4 |a |m | | |
+-----+-----+-------+-------+-------+
Expected output
+-----+-----+-------+-------+-------+
| id |Name |colum1 |colum2 |colum3 |
+-----+-----+-------+-------+-------+
| 2 |b | |y |y |
+-----+-----+-------+-------+-------+
| 4 |a |m |z |x |
+-----+-----+-------+-------+-------+
You can do Self Join.
SELECT T1.*
FROM
table_name T1
INNER JOIN
(SELECT `Name`,MAX(`id`) AS ID FROM table_name GROUP BY `Name` ) T2
ON T1.`id`= T2.`ID` AND T1.`Name` = T2.`Name`
Hope this helps.
I'm not sure if it will work in MS Access. It works in SQL Server. Even if it does, it will be very slow.
SELECT
Groups.Name
,(
SELECT TOP(1) T.colum1
FROM T
WHERE T.Name = Groups.Name AND T.colum1 <> ''
ORDER BY T.ID DESC
) AS C1
,(
SELECT TOP(1) T.colum2
FROM T
WHERE T.Name = Groups.Name AND T.colum2 <> ''
ORDER BY T.ID DESC
) AS C2
,(
SELECT TOP(1) T.colum3
FROM T
WHERE T.Name = Groups.Name AND T.colum3 <> ''
ORDER BY T.ID DESC
) AS C3
FROM
(
SELECT Name
FROM T
GROUP BY Name
) AS Groups

Many to one relation, select only rows which ancestor met all criteria

First I will show You architecture of tables.
Table "public.questionare"
Column | Type |
--------------------+-----------------------+
id | integer |
Table "public.questionareacceptance"
Column | Type |
-------------------------+-----------------------+
id | integer |
questionare_id | integer |
accept_id | integer |
impact_id | integer |
Table questionareacceptance contains:
id | questionare_id | accept_id| impact_id |
----+----------------+----------+------------------+
1 |1 |1 | |
2 |1 |1 | 1 |
3 |1 |1 | 1 |
4 |2 | | 1 |
5 |3 |1 | 1 |
6 |4 |1 | 1 |
7 |4 |1 | 1 |
What I am trying to get is a list of questionare ID where in each questionareacceptance fields accept_id and impact_id are not NULL
My query looks like:
SELECT q.id AS quest,
qa.id AS accepted
FROM questionare q,
questionareacceptance qa
WHERE q.id = qa.questionare_id
AND qa.accept_id IS NOT NULL
AND qa.impact_id IS NOT NULL;
But the result is as fallows:
quest | accepted |
--------------------+-----------------------+
1 |1 |
1 |2 |
1 |3 |
2 |4 |
3 |5 |
4 |6 |
4 |7 |
But the result that should be returned are only 3 and 4 others have impact_id or accept_id null.
Can anyone point me where I am doing the mistake?
your query could be written with not exists:
select
q.id as quest, qa.id as accepted
from questionare as q
inner join questionareacceptance as qa on qa.questionare_id = q.id
where
not exists (
select *
from questionareacceptance as tqa
where
tqa.questionare_id = q.id and
(tqa.accept_id is null or tqa.impact_id is null)
)
but I think faster one would using window functions:
with cte as (
select
q.id as quest, qa.id as accepted,
sum(case when qa.accept_id is not null and qa.impact_id is not null then 1 else 0 end) over(partition by q.id) as cnt1,
count(*) over(partition by q.id) as cnt2
from questionare as q
inner join questionareacceptance as qa on qa.questionare_id = q.id
)
select quest, accepted
from cte
where cnt1 = cnt2
actually looks like you don't need join at all:
with cte as (
select
qa.questionare_id as quest, qa.id as accepted,
sum(case when qa.accept_id is not null and qa.impact_id is not null then 1 else 0 end) over(partition by qa.questionare_id) as cnt1,
count(*) over(partition by qa.questionare_id) as cnt2
from questionareacceptance as qa
)
select quest, accepted
from cte
where cnt1 = cnt2;
sql fiddle demo

How to query multiple COUNT(*) with good performance

I have this table:
CREATE TABLE schedule (
schedule_id serial NOT NULL,
start_date date,
CONSTRAINT schedule_id PRIMARY KEY (schedule_element_id)
)
And this table:
CREATE TABLE schedule_user (
schedule_user_id serial NOT NULL,
schedule_id integer,
state int,
CONSTRAINT fk_schedule_id FOREIGN KEY (schedule_id)
REFERENCES schedule (schedule_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
schedule
-------------------------
|schedule_id |date |
|------------+------------|
|1 |'2013-10-10'|
|2 |'2013-10-20'|
|3 |'2013-08-13'|
-------------------------
schedule_user
-----------------------------------
|schedule_user_id|schedule_id |state|
|----------------+------------+-----|
|1 | 1 |0 |
|2 | 1 |1 |
|3 | 1 |2 |
|4 | 1 |0 |
|5 | 1 |1 |
|6 | 1 |1 |
|4 | 2 |0 |
|5 | 2 |1 |
|7 | 2 |0 |
|2 | 3 |1 |
-----------------------------------
And I want a table like this:
characteristic
---------------------------------------
|schedule_id |state0|state1|state2|total|
|------------+------+------+------+-----|
|1 |2 |3 |1 |6 |
|2 |2 |1 |0 |3 |
|3 |1 |1 |0 |2 |
---------------------------------------
I've made this query that looks as as horrible as it's performance.
SELECT
schedule.schedule_id AS id,
(( SELECT count(*) AS count
FROM schedule_user
WHERE schedule_user.schedule_id = schedule.schedule_id
AND state=0))::integer AS state0,
(( SELECT count(*) AS count
FROM schedule_user
WHERE schedule_user.schedule_id = schedule.schedule_id
AND state=1))::integer AS state1,
(( SELECT count(*) AS count
FROM schedule_user
WHERE schedule_user.schedule_id = schedule.schedule_id
AND state=2))::integer AS state2,
(( SELECT count(*) AS count
FROM schedule_user
WHERE schedule_user.schedule_id = schedule.schedule_id))::integer
AS total
FROM schedule
Is there a better way to perform such a query?
Should I create an Index to 'state' column? if so, how should it look like?
You want to make a pivot table. An easy way to make one in SQL if you know all of the possible values of state beforehand is using sum and case statements.
select schedule_id,
sum(case state when 0 then 1 else 0 end) as state0,
sum(case state when 1 then 1 else 0 end) as state1,
sum(case state when 2 then 1 else 0 end) as state2,
count(*) as total
from schedule_user
group by schedule_id;
Another way is to use the crosstab table function.
Neither of these will let you get away with not knowing the set of values of state (and hence the columns in the result set).
I would try
SELECT s.schedule_id,
COUNT(CASE WHEN su.state = 0 THEN 1 END) AS state0,
COUNT(CASE WHEN su.state = 1 THEN 1 END) AS state1,
COUNT(CASE WHEN su.state = 2 THEN 1 END) AS state2,
COUNT(su.state) AS total
FROM schedule s
LEFT
OUTER
JOIN schedule_user su
ON su.schedule_id = s.schedule_id
GROUP
BY s.schedule_id
;
Ths standard approach is to use SUM() with a CASE over a JOIN with a GROUP BY:
SELECT
schedule.schedule_id AS id,
SUM (case when state=0 then 1 else 0 end) AS state0,
SUM (case when state=1 then 1 else 0 end) AS state1,
SUM (case when state=2 then 1 else 0 end) AS state2,
count(*) AS total
FROM schedule
LEFT JOIN schedule_user
ON schedule_user.schedule_id = schedule.schedule_id
GROUP BY 1