I'm trying to change group of numbers to another one.
Now:
PRACTICE_ID (FK) GROUP_ID ...
0_4_200_400_0.5 4
0_4_300_300_0.5 4
0_4_400_700_0.5 4
0_5_200_400_0.5 5
0_5_900_400_0.5 5
0_5_650_400_0.5 5
Should be:
PRACTICE_ID (FK) GROUP_ID ...
*0_5_200_400_0.5 4
0_5_300_300_0.5 4
0_5_400_700_0.5 4
*0_5_200_400_0.5 5
0_5_900_400_0.5 5
0_5_650_400_0.5 5
I use the query to reach the target:
UPDATE CUSTOM_PRACTICE
SET PRACTICE_ID = REPLACE(PRACTICE_ID, '0_4', '0_5')
WHERE GROUP_ID = 4
In this case I cannot duplicate values. I was trying to use NOT EXISTS clause but didn't get result
How can I rewrite all values which have not duplicate? Like this:
PRACTICE_ID (FK) GROUP_ID ...
0_4_200_400_0.5 4
0_5_300_300_0.5 4
0_5_400_700_0.5 4
0_5_200_400_0.5 5
0_5_900_400_0.5 5
0_5_650_400_0.5 5
UPDATE CUSTOM_PRACTICE
SET PRACTICE_ID = REPLACE(PRACTICE_ID, '0_4', '0_5')
WHERE GROUP_ID = 4 AND REPLACE(PRACTICE_ID, '0_4', '0_5') NOT IN
(SELECT PRACTICE_ID FROM CUSTOM_PRACTICE)
SQL Server has a very convenient function for this called STUFF(). One way of doing what you want uses NOT EXISTS:
UPDATE CUSTOM_PRACTICE
SET PRACTICE_ID = STUFF(PRACTICE_ID, 3, 1, '5')
WHERE PRACTICE_ID LIKE '0_4%' AND
NOT EXISTS (SELECT 1
FROM CUSTOM_PRACTICE CP2
WHERE CP2.PRACTICE_ID = STUFF(PRACTICE_ID, 3, 1, '5') AND
STUFF(PRACTICE_ID, 3, 1, '5')
);
Note that REPLACE(PRACTICE_ID, '0_4', '0_5') does not really do what yo want, because:
REPLACE('0_4_200_400_0.5', '0_4', '0_5')
returns:
'0_5_200_500_0.5'
not:
'0_4_200_400_0.5'
However, I think I like using an updatable CTE, because the logic only appears in one place:
with toupdate as (
select cp.*
(case when practice_id like '0_4%' then stuff(practice_id, 3, 1, '5')
else practice_id
end) as new_practice_id
from custom_practice cp
)
update t
set practice_id = new_practice_id
from (select t.*, count(*) over (partition by new_practice_id) as cnt
from toupdate
)
where practice_id <> new_practice_id and cnt = 1;
Related
Let's say I am returning the following table from a select
CaseId
DocId
DocumentTypeId
DocumentType
ExpirationDate
1
1
1
I797
01/02/23
1
2
2
I94
01/02/23
1
3
3
Some Other Value
01/02/23
I want to select ONLY the row with DocumentType = 'I797', then if there is no 'I797', I want to select ONLY the row where DocumentType = 'I94'; failing to find either of those two I want to take all rows with any other value of DocumentType.
Using SQL Server ideally.
I think I'm looking for an XOR clause but can't work out how to do that in SQL Server or to get all other values.
Similar to #siggemannen answer
select top 1 with ties
case when DocumentType='I797' then 1
when DocumentType='I94' then 2
else 3
end gr
,docs.*
from docs
order by
case when DocumentType='I797' then 1
when DocumentType='I94' then 2
else 3
end
Shortest:
select top 1 with ties
docs.*
from docs
order by
case when DocumentType='I797' then 1
when DocumentType='I94' then 2
else 3
end
Something like this perhaps:
select *
from (
select t.*, DENSE_RANK() OVER(ORDER BY CASE WHEN DocumentType = 'I797' THEN 0 WHEN DocumentType = 'I94' THEN 1 ELSE 2 END) AS prioorder
from
(
VALUES
(1, 1, 1, N'I797', N'01/02/23')
, (1, 2, 2, N'I94', N'01/02/23')
, (1, 3, 3, N'Some Other Value', N'01/02/23')
, (1, 4, 3, N'Super Sekret', N'01/02/23')
) t (CaseId,DocId,DocumentTypeId,DocumentType,ExpirationDate)
) x
WHERE x.prioorder = 1
The idea is to rank rows by 1, 2, 3 depending on document type. Since we rank "the rest" the same, you will get all rows if I797 and I94 is missing.
select * from YourTable where DocumentType = 'I797'
union
select * from YourTable t where DocumentType = 'I94' and (not exists (select * from YourTable where DocumentType = 'I797'))
union
select * from YourTable t where (not exists (select * from YourTable where DocumentType = 'I797' or DocumentType = 'I94' ))
After every group / row i want to insert a hardcoded dummy row with a bunch of 'xxxx' to act a separator.
I would like to use oracle sql to do this query. i can execute it using a loop but i don't want to use plsql.
As the others suggest, it is best to do it on the front end.
However, if you have a burning need to be done as a query, here is how.
Here I did not use the rownum function as you have already done. I assume, your data is returned by a query, and you can replace my table with your query.
I made few more assumptions, as you have data with row numbers in it.
[I am not sure what do you mean by not PL/SQL]
Select Case When MOD(rownm, 2) = 0 then ' '
Else to_char((rownm + 1) / 2) End as rownm,
name, total, column1
From
(
select (rownm * 2 - 1) rownm,name, to_char(total) total ,column1 from t
union
SELECT (rownm * 2) rownm,'XXX' name, 'XXX' total, 'The row act .... ' column1 FROM t
) Q
Order by Q.rownm;
and here is the fiddle
Since you're already grouping the data, it might be easier to use GROUPING SETS instead of a UNION.
Grouping sets let you group by multiple sets of columns, including the same set twice to duplicate rows. Then the GROUP_ID function can be used to determine when the fake values should be used. This code will be a bit smaller than a UNION approach, and should be faster since it doesn't need to reference the table multiple times.
select
case when group_id() = 0 then name else '' end name,
case when group_id() = 0 then sum(some_value) else null end total,
case when group_id() = 1 then 'this rows...' else '' end column1
from
(
select 'jack' name, 22 some_value from dual union all
select 'jack' name, 1 some_value from dual union all
select 'john' name, 44 some_value from dual union all
select 'john' name, 1 some_value from dual union all
select 'harry' name, 1 some_value from dual union all
select 'harry' name, 1 some_value from dual
) raw_data
group by grouping sets (name, name)
order by raw_data.name, group_id();
You can use row generator technique (using CONNECT BY) and then use CASE..WHEN as follows:
SQL> SELECT CASE WHEN L.LVL = 1 THEN T.ROWNM END AS ROWNM,
2 CASE WHEN L.LVL = 1 THEN T.NAME
3 ELSE 'XXX' END AS NAME,
4 CASE WHEN L.LVL = 1 THEN TO_CHAR(T.TOTAL)
5 ELSE 'XXX' END AS TOTAL,
6 CASE WHEN L.LVL = 1 THEN T.COLUMN1
7 ELSE 'This row act as separator..' END AS COLUMN1
8 FROM T CROSS JOIN (
9 SELECT LEVEL AS LVL FROM DUAL CONNECT BY LEVEL <= 2
10 ) L ORDER BY T.ROWNM, L.LVL;
ROWNM NAME TOTAL COLUMN1
---------- ---------- ----- ---------------------------
1 Jack 23
XXX XXX This row act as separator..
2 John 45
XXX XXX This row act as separator..
3 harry 2
XXX XXX This row act as separator..
4 roy 45
XXX XXX This row act as separator..
5 Jacob 26
XXX XXX This row act as separator..
10 rows selected.
SQL>
I have no idea why I haven't found a solution to this on here but: I am trying to COUNT how many times the Serial_Parent appears during the packing phase so I can use this in other calculations.
This works perfectly:
SELECT
Serial_Parent, COUNT(Serial_Parent)
FROM
mfng_data
WHERE
Machine_Process = 'Packing'
GROUP BY
Serial_Parent;
However when I try to UPDATE a column with this COUNT, I fail miserably as it just counts all the rows in the table and saves that as each row value thus 2,134,222 appearing in each row value.
I've tried this:
UPDATE mfng_data
SET Count_Serial_Parent = (SELECT COUNT(*)
FROM mfng_data
WHERE Machine_Process = 'Packing'
AND Serial_Parent = Serial_Parent)
WHERE Serial_Parent = Serial_Parent;
I've also tried this:
UPDATE mfng_data
SET Count_Serial_Parent = (SELECT COUNT(Serial_Parent)
FROM mfng_data
WHERE Machine_Process = 'Packing'
AND Serial_Parent = Serial_Parent);
Sample data:
Spec: 12373 Rev: -6 M35846 M358461 M3584610 M35846101 NULL NULL NULL M35846101 6808
Spec: 12373 Rev: -6 M35846 M358461 M3584610 M35846102 NULL NULL NULL M35846102 6808
Spec: 16692 Rev: -4 K45678 K456781 K4567810 K45678101 NULL NULL NULL K45678101 3964
Spec: 16692 Rev: -4 K45678 K456782 K4567820 K45678201 NULL NULL NULL K45678201 3978
Spec: 16693 Rev: -4 K45678 K456782 K4567820 K45678202 NULL NULL NULL K45678202 3806
Desired result (M35846 will appear twice so it will list "2" for each row entry)
Serial_Parent Count_Serial_Parent
----------------------------------
M35846 2
M35846 2
J39384 52 - - > 52 rows will show "52" and so on below
M35488 10
K4448 4
M35927 8
K45678 3
You need table aliases and qualified column names:
UPDATE m
SET Count_Serial_Parent = (SELECT COUNT(*)
FROM mfng_data m2
WHERE m2.Machine_Process = 'Packing' AND
m2.Serial_Parent = m.Serial_Parent
)
FROM mfng_data m;
The subquery needs to be correlated to the outer query. A condition such as Serial_Parent = Serial_Parent basically always evaluates to true because it is referring to the column of the table referenced in the subquery.
However, the better approach is an updatable CTE:
with toupdate as (
select m.*,
sum(case when m.Machine_Process = 'Packing' then 1 else 0 end) over (partition by m.serial_parent) as new_Count_Serial_Parent
from mfng_data m
)
update toupdate
set Count_Serial_Parent = new_Count_Serial_Parent;
Another way (using a toy table -- hope you get the idea):
DECLARE # TABLE (Serial_Parent int, Count_Serial_Parent int)
INSERT INTO # (Serial_Parent, Count_Serial_Parent) VALUES
(1,0), (1,0),(2,0),(2,0),(2,0)
SELECT * FROM #
UPDATE m -- mfng_data
SET m.Count_Serial_Parent = j.Count_Serial_Parent
FROM # m -- mfng_data
JOIN (
SELECT Serial_Parent, COUNT(*) Count_Serial_Parent
FROM #
GROUP BY Serial_Parent
) j ON j.Serial_Parent = m.Serial_Parent
SELECT * FROM #
I have a table like below:
CREATE TABLE public.test_table
(
"ID" serial PRIMARY KEY NOT NULL,
"CID" integer NOT NULL,
"SEG" integer NOT NULL,
"DDN" character varying(3) NOT NULL
)
and data looks like this:
ID CID SEG DDN
1 1 1 "711"
2 1 2 "800"
3 1 3 "124"
4 2 1 "711"
5 3 1 "711"
6 3 2 "802"
7 4 1 "799"
8 5 1 "799"
9 5 2 "804"
10 6 1 "799"
I need to group these data by CID column and get column counts depends on DDN columns first values but counts must give me two different information, if it's more than 1 or not.
I'm really sorry if couldn't explains clearly. Let me show you what I need..
DDN END TRA
711 1 2
799 2 1
As you can see, DDN:711 has 1 record of single count (ID:4). This is END column.
But 2 times has multiple SEG count (ID:1to3 and ID:5to6). This is TRA column.
I can not be sure what column should be in group clause!
My solution:
Just found a solution like below
WITH x AS (
SELECT
(SELECT t1."DDN" FROM public.test_table AS t1
WHERE t1."CID"=t."CID" AND t1."SEG"=1) AS ddn,
COUNT("CID") AS seg_count
FROM public.test_table AS t
GROUP BY "CID"
)
SELECT ddn, COUNT(seg_count) AS "TOTAL",
SUM(CASE WHEN x.seg_count=1 THEN 1 ELSE 0 END) as "END",
SUM(CASE WHEN x.seg_count>1 THEN 1 ELSE 0 END) as "TRA"
FROM x
GROUP BY ddn;
Equivalent, faster query:
SELECT "DDN"
, COUNT(*) AS "TOTAL"
, COUNT(*) FILTER (WHERE seg_count = 1) AS "END"
, COUNT(*) FILTER (WHERE seg_count > 1) AS "TRA"
FROM (
SELECT DISTINCT ON ("CID")
"DDN" -- assuming min "SEG" is always 1
, COUNT(*) OVER (PARTITION BY "CID") AS seg_count
FROM test_table
ORDER BY "CID", "SEG"
) sub
GROUP BY "DDN";
db<>fiddle here
Notes
CTEs are typically slower and should only be used where needed in Postgres.
This is equivalent to the query in the question assuming that the minimum "SEG" per "CID" is always 1 - since this query returns the row with the minimum "SEG" while your query returns the one with "SEG" = 1. Typically, you would want the "first" segment and my query implements this requirement more reliably, but that's not clear from the question.
COUNT(*) is slightly faster than COUNT(column) and equivalent while not involving NULL values (applicable here). Related:
PostgreSQL: running count of rows for a query 'by minute'
About DISTINCT ON:
Select first row in each GROUP BY group?
The aggregate FILTER syntax requires Postgres 9.4+:
Conditional SQL count
Here is the solution i propose, the query can be simplified i guess.
CREATE TABLE test_table
(
ID serial PRIMARY KEY NOT NULL,
CID integer NOT NULL,
SEG integer NOT NULL,
DDN character varying(3) NOT NULL
);
insert into test_table(CID,SEG,DDN)
values
( 1, 1, '711'),
( 1, 2, '800'),
( 1, 3, '124'),
( 2, 1, '711'),
( 3, 1, '711'),
( 3, 2, '802'),
( 4, 1, '799'),
( 5, 1, '799'),
( 5, 2, '804'),
( 6, 1, '799');
with summary as (with ddn_t as (select cid,ddn,row_number() OVER( PARTITION BY cid)from test_table)
select a.cid,count(distinct a.ddn),b.ddn
from ddn_t a
join ddn_t b on b.cid=a.cid and b.row_number=1
group by a.cid, b.ddn)
select ddn,
sum (case when count >1 then 1 else 0 end) as TRA,
sum (case when count = 1 then 1 else 0 end) as END
from summary
group by ddn;
I have a table Fields. Example:-
id remote unique_id
1 23 30007
1 24 30008
1 1 30009
2 4 30007
2 5 30008
2 1 30009
3 6 30007
3 7 30008
3 2 30009
here i want to get sum of unique_id field=30008 remote value where unique_id field=30009 remote value = 1;
basically, what i want is:
(query 1)
select SUM(remote) from Fields where unquie_id = '30008';
and one added filter where:(query 2)
select remote from Fields where unquie_id = '30009';
i want to select the _id record of query 2 where remote=1 for my query 1.
So the above output would be: 24+5=29.
Here i am selecting _id as 1,2. _id =3 would be rejected as it remote for unique_id = 30009 is 2.
This might be simple for you guys but i am new with sqlite. Hence please help.
If I understand your question correctly, then you want to sum the remote column where the unique_id is 30008, but only for those id groups having remote=1 and unique_id=30009.
SELECT SUM(f1.remote)
FROM Fields f1
INNER JOIN
(
SELECT id
FROM Fields
GROUP BY id
HAVING SUM(CASE WHEN unique_id = '30009' AND remote = 1 THEN 1 ELSE 0 END) > 0
) f2
ON f1.id = f2.id
WHERE unique_id = '30008';
I didn't actually run it, but something like this should do what you need:
select SUM(remote) from Fields as F where F.unique_id = '30008' and F.id in (select remote from Fields as G where G.unique_id = '30009');
So I tested it:
create table Fields(id integer, remote integer, unique_id integer);
insert into Fields(id, remote, unique_id) values (1, 23, 3007), (1, 24, 30008), (1 ,1 ,30009), (2, 4, 30007), (2 , 5 , 30008), (2 , 1 , 30009), (3 , 6, 30007), (3 , 7, 30008
), (3 , 2 , 30009);
/*select * from Fields;*/
select SUM(remote) from Fields as F where F.unique_id = '30008' and F.id in (select remote from Fields as G where G.unique_id = '30009');
And the ouput was: 29 as desired.
You could use exists
select sum(remote) total
from Fields f
where exists (
select 1 from Fields
where remote = f.id and unique_id = 30009
) and f.unique_id = 30008