Arrange NULL at the end in each column of the table - sql

I have a table that contains two columns col1, col2.
NULL need to be placed at the end of each column.
Data in my table now.
--- ---
col1 col2
--- ---
1 null
2 2
3 null
4 4
null 5
create table mytable (
col1 number(3),
col2 number(3)
);
insert into mytable values(1, null);
insert into mytable values(2, 2);
insert into mytable values(3, null);
insert into mytable values(4, 4);
insert into mytable values(null, 5);
I need to update them in this format.
--- ---
col1 col2
--- ---
1 2
2 4
3 5
4 null

This mostly solves the problem; you'll need to put the final update in a recursive trigger so it executes the proper number of times.
-- remove nulls from col2
delete from mytable where col2 is null;
-- erase col1
update mytable
set col1 = null;
-- add "footer" row
insert into mytable (col1,col2) values ((select count(*)+1 from mytable), null);
-- update col1 for 1 remaining record
update mytable
set col1 = (select count(*) from mytable where col1 is not null)
where col2 = (select min(col2) from mytable where col1 is null);

Related

Get ID of inserted & Selected row after multiple insert [duplicate]

This question already has answers here:
Insert Into... Merge... Select (SQL Server)
(1 answer)
Combine OUTPUT inserted.id with value from selected row
(2 answers)
Closed 4 years ago.
I have a query running in loop which I am trying to optimize as this
INSERT INTO myTable (col1, col2, col3)
OUTPUT inserted.id, SOURCE_ROW_ID_NEEDED_HERE
SELECT col1, col2, col3
FROM myTable
WHERE col2 = 20 --any value
My problem is : col2 = 20 can have N number of rows which get inserted, I need the id of the source row for the new record. For example say there are 3 rows for col2 = 20 and id of them are 11,12,15. The new inserted ID are say 150,151,152.
I would need
11 150
12 151
15 152
Are you looking for something like
CREATE TABLE T1(
Col1 INT IDENTITY(1, 1),
Col2 INT,
Col3 INT
);
CREATE TABLE T2(
Col1 INT IDENTITY(1, 1),
Col2 INT,
Col3 INT
);
INSERT INTO T2(Col2, Col3) VALUES
(11, 150),
(12, 151),
(15, 152);
DECLARE #TT TABLE (ID INT, Col2 INT);
SET IDENTITY_INSERT T1 ON;
INSERT INTO T1 (Col1, Col2, Col3)
OUTPUT INSERTED.Col1,
INSERTED.Col2
INTO #TT (ID, Col2)
SELECT Col1,
Col2,
Col3
FROM T2;
SELECT *
FROM #TT;
SET IDENTITY_INSERT T1 OFF;
Demo

Update a target table only when source data are not null and different

I have 2 tables with the same columns. The first table Temp1 is loaded with a sp and is temporary. It is used to load a table T1 on a production environment. By example, I have this data :
Table Temp1
Id Col1 Col2 Col3 Col4 Col5
2 null null 0.5 null 0.6
3 0.1 null null null null
Table T1
Id Col1 Col2 Col3 Col4 Col5
1 2 3 0.4 5 0.6
2 5 4 6 4 7
3 8 9 7 10 1
I need to update T1 with data from Temp1 only when columns from Temp1 are not null and have not the same value in T1 (bold values in my example).
I’m stuck with this problem. Any ideas please ?
Thanks,
Using an update statement with a case expression would be something like this.
update tt1
set Col1 = case when T.Col1 is not null and T.Col1 <> tt1.Col1 then T.Col1 else tt1.Col1 end
from T1 tt1
join Temp1 t on t.Id = tt1.Id
UPDATE T1
SET T1.Col1 = ISNULL(NULLIF(ISNULL(tmp.Col1 , T1.Col1) , T1.Col1) , T1.Col1)
, T1.Col2 = ISNULL(NULLIF(ISNULL(tmp.Col2 , T1.Col2) , T1.Col2) , T1.Col2)
, T1.Col3 = ISNULL(NULLIF(ISNULL(tmp.Col3 , T1.Col3) , T1.Col3) , T1.Col3)
, T1.Col4 = ISNULL(NULLIF(ISNULL(tmp.Col4 , T1.Col4) , T1.Col4) , T1.Col4)
, T1.Col5 = ISNULL(NULLIF(ISNULL(tmp.Col5 , T1.Col5) , T1.Col5) , T1.Col5)
FROM Temp1 tmp
INNER JOIN T1 ON t1.ID = tmp.ID
you can use a merge statement for this problem.
CREATE TABLE #t
(
ID int IDENTITY(1,1),
Name NVARCHAR(50),
Salary DECIMAL(10,2)
)
INSERT INTO #t (Name, Salary) VALUES('abc', 123.4)
INSERT INTO #t (Name, Salary) VALUES('pqa', 127.4)
INSERT INTO #t (Name, Salary) VALUES('xyz', 233.4)
CREATE TABLE #tempForT
(
ID int IDENTITY(1,1),
Name NVARCHAR(50),
Salary DECIMAL(10,2)
)
INSERT INTO #tempForT (Name, Salary) VALUES('abc', 200.4)
INSERT INTO #tempForT (Name, Salary) VALUES('pqa', 200.4)
INSERT INTO #tempForT (Name, Salary) VALUES('xyz', NULL)
SELECT * FROM #t
SELECT * FROM #tempForT
Here is the Solution
MERGE #t AS DestTable
USING (
SELECT
NAME,
Salary
FROM #tempForT
)SourceTable
ON DestTable.Name = SourceTable.Name --Should contain columns like composite primary key
--AND other columns
WHEN MATCHED
THEN
UPDATE
SET DestTable.Salary = ISNULL(SourceTable.Salary, DestTable.Salary) --checking for NULL
;
SELECT * FROM #t
Note: If #tempForT contains new values (rows), then those rows will not get inserted for that you have to update the merge statement with WHEN NOT MATCHED in that case insert new records.

How to know the column name from a table based on the column values

I am working in Informix and I want to know if there is a simple way to know the tabname/colname by its possible column values.
For example:
table1
Register 1
==========
id 1
col1 3
col2 Y
Register 2
==========
id 2
col1 43
col2 X
Register 3
==========
id 2
col1 0
col2 Z
Register 4
==========
id 2
col1 23
col2 F
table2
Register 1
==========
id 1
col1 X
col2 Y
Register 2
==========
id 2
col1 X
col2 X
Register 3
==========
id 2
col1 Z
col2 Z
Register 4
==========
id 2
col1 X
col2 X
table3
Register 1
==========
id 1
col1 ASX
With this database, if I want to know the colnames and their related tabnames of the database that contain X, Y and Z (amoung other values).
It could be something like this:
select tabname, colname
where ('X','Y','Z') in colnamevalues --this has been invented by me
And this should return the following values:
table1.col2
table2.col1
table2.col2
--Note that the columns fetched contains also other values
--different from 'X', 'Y' and 'Z' but T didn't fix in this case
--the whole list of values, only some of them
I have queried for other Q&A but all of them look to use some functions of other databases such as Oracle or SQL Server and I don't understand them very well.
You can get all the tables that exist on a database by querying the systables:
SELECT tabname
FROM systables
WHERE tabtype = 'T' --get only tables
AND tabid > 99; --skip catalog tables
You can join it to the syscolumns table to get the columns:
SELECT t.tabname, c.colname
FROM systables t
INNER JOIN syscolumns c ON (c.tabid = t.tabid)
WHERE t.tabtype = 'T' AND t.tabid > 99;
And if you know the type of values you can even filter it. Example if you're looking for "strings":
SELECT t.tabname, c.colname
FROM systables t
INNER JOIN syscolumns c ON (c.tabid = t.tabid)
WHERE t.tabtype = 'T' AND t.tabid > 99
AND MOD(c.coltype,256) IN (
0, --CHAR
13, --VARCHAR
15, --NCHAR
16, --NVARCHAR
40, --LVARCHAR
43 --LVARCHAR
);
The next example works, but it really should be optimized and bullet proof, but can get you kick off.
When I have time I get another look at it and check what can be optimized and put some error handling.
Another way to do it is scripting, what OS are you running?
Schema creation:
CREATE TABLE tab1(
id INT,
col1 CHAR(3),
col2 CHAR(3)
);
INSERT INTO tab1 VALUES (1, 3, 'Y');
INSERT INTO tab1 VALUES (2, 43, 'X');
INSERT INTO tab1 VALUES (2, 0, 'Z');
INSERT INTO tab1 VALUES (2, 23, 'F');
CREATE TABLE tab2(
id INT,
col1 CHAR(3),
col2 CHAR(3)
);
INSERT INTO tab2 VALUES (1, 'X', 'Y');
INSERT INTO tab2 VALUES (2, 'X', 'X');
INSERT INTO tab2 VALUES (2, 'Z', 'Z');
INSERT INTO tab2 VALUES (2, 'X', 'X');
CREATE TABLE tab3(
id INT,
col1 CHAR(3)
);
INSERT INTO tab3 VALUES (1, 'ASX');
Sample function:
CREATE FUNCTION get_columns()
RETURNING LVARCHAR(257) AS col;
DEFINE stmt VARCHAR(255);
DEFINE tab_name VARCHAR(128,0);
DEFINE tab_id INTEGER;
DEFINE col_name VARCHAR(128,0);
DEFINE o_tname VARCHAR(128,0);
DEFINE o_cname VARCHAR(128,0);
CREATE TEMP TABLE out_table(
t_name VARCHAR(128,0),
c_name VARCHAR(128,0)
);
CREATE TEMP TABLE tab_v (
col1 VARCHAR(255)
);
INSERT INTO tab_v VALUES ('X');
INSERT INTO tab_v VALUES ('Y');
INSERT INTO tab_v VALUES ('Z');
FOREACH tables FOR
SELECT tabname, tabid
INTO tab_name, tab_id
FROM systables
WHERE tabid > 99 AND tabtype = 'T'
FOREACH column FOR
SELECT colname
INTO col_name
FROM syscolumns
WHERE tabid = tab_id
AND MOD(coltype,256) IN (
0, --CHAR
13, --VARCHAR
15, --NCHAR
16, --NVARCHAR
40, --LVARCHAR
43 --LVARCHAR
)
LET stmt = "INSERT INTO out_table "||
"SELECT '"||tab_name||"', '"||col_name||"' "||
"FROM "||tab_name||" "||
"WHERE EXISTS (SELECT 1 FROM tab_v v WHERE v.col1 = "||col_name||");";
EXECUTE IMMEDIATE stmt;
END FOREACH
END FOREACH
FOREACH out FOR
SELECT UNIQUE t_name, c_name
INTO o_tname, o_cname
FROM out_table
RETURN o_tname||"."||o_cname WITH RESUME;
END FOREACH
DROP TABLE out_table;
DROP TABLE tab_v;
END FUNCTION;
EXECUTE FUNCTION get_columns();

finding counts assigned to another field's value

Create table t1 (col1 (number), col2 (number), col3 (number);
Insert into t1 values (1,1,1);
Insert into t1 values (1,2,5);
Insert into t1 values (1,3,1);
Insert into t1 values (2,1,1);
Insert into t1 values (2,1,1);
Desired result
col1 col2
1 3
2 2
I need to return the value in col1 and the count of values found in col 2 for each distinct col1 value. Do not need col3
select col1, count(col1) from t1
group by col1

Merge with same target and source table

I have one table where I want to check if record exists leave it alone, if not insert new row and update previous row. I am wondering if I can use merge here like below ?
CREATE TABLE a
(keycol INT PRIMARY KEY,
col1 INT NOT NULL,
col2 INT NOT NULL,
col3 INT NOT NULL);
INSERT INTO a VALUES (1,0,0,0),(2,0,0,0);
MERGE INTO a
USING select 1 from a where col1 = 3
WHEN NOT MATCHED THEN
UPDATE SET
col2 = 2,
col2 = 2,
col3 = 2
where col1 = 3
WHEN NOT MATCHED THEN
INSERT (keycol, col1, col2, col3)
VALUES (4, 0, 0, 0)
Thanks,
MERGE INTO a
USING (
VALUES (3,3,2,2),
(4,0,0,0)
) AS source (keycol, col1, col2, col3)
ON a.keycol = source.keycol
AND a.col1 = source.col1
WHEN MATCHED THEN
UPDATE
SET col2 = source.col2,
col3 = source.col3
WHEN NOT MATCHED THEN
INSERT (keycol, col1, col2, col3)
VALUES (keycol, col1, col2, col3);