SQL query for insert into with a set of constants - select

It seems like there should be a query for this, but I can't think of how to do it.
I've got a table with a composite primary key consisting of two fields I'd like to populate with data,
I can do an insert into from one table to fill up half the keys, but I want to fill up the other half with a set of constants (0, 3, 5, 6, 9) etc...
so the end result would look like this
+--------------+
|AwesomeTable |
+--------------+
| Id1 | Id2 |
| 1 | 0 |
| 1 | 3 |
| 1 | 5 |
| 1 | 6 |
| 1 | 9 |
| 2 | 0 |
| 2 | 3 |
| ... | ... |
+--------------+
I've got as far as insert into awesometable (id1, id2) select id1, [need something here] from table1 [need something else here]

I've got a table with 2 primary keys
No, you don't. A table can only have one primary key. You probably mean a composite primary key.
I believe you want this:
INSERT
INTO awesometable (id1, id2)
SELECT t1.id1, q.id2
FROM table1 t1
CROSS JOIN
(
SELECT 0 AS id2
UNION ALL
SELECT 3
UNION ALL
SELECT 5
UNION ALL
SELECT 6
UNION ALL
SELECT 9
) q
, or in Oracle:
INSERT
INTO awesometable (id1, id2)
SELECT t1.id1, q.id2
FROM table1 t1
CROSS JOIN
(
SELECT 0 AS id2
FROM dual
UNION ALL
SELECT 3
FROM dual
UNION ALL
SELECT 5
FROM dual
UNION ALL
SELECT 6
FROM dual
UNION ALL
SELECT 9
FROM dual
) q

If I understand correctly, maybe you can use something like this:
insert into awesometable (id1, id2)
select id1, (select top 1 id2 from table2 where /*a condition here to retreive only one result*/)
from table1

Related

Maintaining order in DB2 "IN" query

This question is based on this one. I'm looking for a solution to that question that works in DB2. Here is the original question:
I have the following table
DROP TABLE IF EXISTS `test`.`foo`;
CREATE TABLE `test`.`foo` (
`id` int(10) unsigned NOT NULL auto_increment,
`name` varchar(45) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Then I try to get records based on the primary key
SELECT * FROM foo f where f.id IN (2, 3, 1);
I then get the following result
+----+--------+
| id | name |
+----+--------+
| 1 | first |
| 2 | second |
| 3 | third |
+----+--------+
3 rows in set (0.00 sec)
As one can see, the result is ordered by id. What I'm trying to achieve is to get the results ordered in the sequence I'm providing in the query. Given this example it should return
+----+--------+
| id | name |
+----+--------+
| 2 | second |
| 3 | third |
| 1 | first |
+----+--------+
3 rows in set (0.00 sec)
You could use a derived table with the IDs you want, and the order you want, and then join the table in, something like...
SELECT ...
FROM mcscb.mcs_premise prem
JOIN mcscb.mcs_serv_deliv_id serv
ON prem.prem_nb = serv.prem_nb
AND prem.tech_col_user_id = serv.tech_col_user_id
AND prem.tech_col_version = serv.tech_col_version
JOIN (
SELECT 1, '9486154876' FROM SYSIBM.SYSDUMMY1 UNION ALL
SELECT 2, '9403149581' FROM SYSIBM.SYSDUMMY1 UNION ALL
SELECT 3, '9465828230' FROM SYSIBM.SYSDUMMY1
) B (ORD, ID)
ON serv.serv_deliv_id = B.ID
WHERE serv.tech_col_user_id = 'CRSSJEFF'
AND serv.tech_col_version = '00'
ORDER BY B.ORD
You can use derived column to do custom ordering.
select
case
when serv.SERV_DELIV_ID = '9486154876' then 1 ELSE
when serv.SERV_DELIV_ID = '9403149581' then 2 ELSE 3
END END as custom_order,
...
...
ORDER BY custom_order
To make the logic a little bit more evident you might modify the solution provided by bhamby like so:
WITH ordered_in_list (ord, id) as (
VALUES (1, '9486154876'), (2, '9403149581'), (3, '9465828230')
)
SELECT ...
FROM mcscb.mcs_premise prem
JOIN mcscb.mcs_serv_deliv_id serv
ON prem.prem_nb = serv.prem_nb
AND prem.tech_col_user_id = serv.tech_col_user_id
AND prem.tech_col_version = serv.tech_col_version
JOIN ordered_in_list il
ON serv.serv_deliv_id = il.ID
WHERE serv.tech_col_user_id = 'CRSSJEFF'
AND serv.tech_col_version = '00'
ORDER BY il.ORD

SQL to remove rows with duplicated value while keeping one

Say I have this table
id | data | value
-----------------
1 | a | A
2 | a | A
3 | a | A
4 | a | B
5 | b | C
6 | c | A
7 | c | C
8 | c | C
I want to remove those rows with duplicated value for each data while keeping the one with the min id, e.g. the result will be
id | data | value
-----------------
1 | a | A
4 | a | B
5 | b | C
6 | c | A
7 | c | C
I know a way to do it is to do a union like:
SELECT 1 [id], 'a' [data], 'A' [value] INTO #test UNION SELECT 2, 'a', 'A'
UNION SELECT 3, 'a', 'A' UNION SELECT 4, 'a', 'B'
UNION SELECT 5, 'b', 'C' UNION SELECT 6, 'c', 'A'
UNION SELECT 7, 'c', 'C' UNION SELECT 8, 'c', 'C'
SELECT * FROM #test WHERE id NOT IN (
SELECT MIN(id) FROM #test
GROUP BY [data], [value]
HAVING COUNT(1) > 1
UNION
SELECT MIN(id) FROM #test
GROUP BY [data], [value]
HAVING COUNT(1) <= 1
)
but this solution has to repeat the same group by twice (consider the real case is a massive group by with > 20 columns)
I would prefer a simpler answer with less code as oppose to complex ones. Is there any more concise way to code this?
Thank you
You can use one of the methods below:
Using WITH CTE:
WITH CTE AS
(SELECT *,RN=ROW_NUMBER() OVER(PARTITION BY data,value ORDER BY id)
FROM TableName)
DELETE FROM CTE WHERE RN>1
Explanation:
This query will select the contents of the table along with a row number RN. And then delete the records with RN >1 (which would be the duplicates).
This Fiddle shows the records which are going to be deleted using this method.
Using NOT IN:
DELETE FROM TableName
WHERE id NOT IN
(SELECT MIN(id) as id
FROM TableName
GROUP BY data,value)
Explanation:
With the given example, inner query will return ids (1,6,4,5,7). The outer query will delete records from table whose id NOT IN (1,6,4,5,7).
This fiddle shows the records which are going to be deleted using this method.
Suggestion: Use the first method since it is faster than the latter. Also, it manages to keep only one record if id field is also duplicated for the same data and value.
I want to add MYSQL solution for this query
Suggestion 1 : MySQL prior to version 8.0 doesn't support the WITH clause
Suggestion 2 : throw this error (you can't specify table TableName for update in FROM clause
So the solution will be
DELETE FROM TableName WHERE id NOT IN
(SELECT MIN(id) as id
FROM (select * from TableName) as t1
GROUP BY data,value) as t2;

SQL Selecting Maximum Based on Minor-Major scheme

I am trying to create a query that will select a DISTINCT line, select using a revision Minor / Major scheme. Below is an example table:
Serial Number | RevMajor | RevMinor
-----------------------------------
AQ155 | 1 | 1
AQ155 | 1 | 2
AQ155 | 1 | 1
AQ155 | 1 | 7
AQ155 | 2 | 1 <---------
JR2709 | 1 | 7
JR2709 | 2 | 2 <---------
How can I write a query in T-SQL 2008 that will select only the two highlighted lines, the "Newest Revision"?
Thanks in advance!
You could
select * from (
select *, row_number() over (partition by [Serial Number] order by RevMajor desc, RevMinor desc) VersionRank
from table
) T
where VersionRank = 1
select [serial number], revmajor, revminor
from table1
where revMajor = (select max(revmajor) from table1)
another way to do this could be:
select [serial number], revmajor, revminor
from table1 a
inner join ( select max(revMajor) from table1 ) b on a.revmajor = b.revmajor
Another way if you know there are only 2 rows:
select top 2 [serial number], revmajor, revminor
from table1 a
order by revmajor desc, revminor desc

T SQL Question: How to apply changes to similar rows based on one row's value

In my SQL Server 2008 DB, I have a table with records sort of like this:
ID 1 | Group1 | \ftp\path\group1\file1.txt
ID 2 | Group1 | C:\local\file\path\group1\file1.txt
ID 3 | Group1 | C:\local\file\path\group1\file1.txt
ID 4 | Group1 | C:\local\file\path\group1\file1.txt
ID 5 | Group2 | \ftp\path\group2\file1.txt
ID 6 | Group2 | C:\local\file\path\group2\file1.txt
ID 7 | Group2 | C:\local\file\path\group2\file1.txt
I need to update the table to look like this:
ID 1 | Group1 | \ftp\path\group1\file1.txt
ID 2 | Group1 | \ftp\path\group1\file1.txt
ID 3 | Group1 | \ftp\path\group1\file1.txt
ID 4 | Group1 | \ftp\path\group1\file1.txt
ID 5 | Group2 | \ftp\path\group2\file1.txt
ID 6 | Group2 | \ftp\path\group2\file1.txt
ID 7 | Group2 | \ftp\path\group2\file1.txt
I just don't know how to start this. It's easy for me to find the values in the third column, because they match this wildcard: %:\%.
So, I'm trying to replace the value in those fields that match that wildcard with the correct value in a record that does not match that wildcard. Damn, it's so hard to explain it.
I'm probably doing a poor job of explaining this issue but the right words are eluding me at the moment.
Any ideas? I appreciate the help.
This gets the results you show, but I don't think the rules I applied match the way you described how you got here. You're talking about a wildcard '%:\%' but I see nothing in any of the data that looks anything like that.
DECLARE #foo TABLE
(
ID VARCHAR(32) PRIMARY KEY,
[Group] VARCHAR(32),
Val VARCHAR(32)
);
INSERT #foo SELECT 'ID 1','Group1','Value 1'
UNION ALL SELECT 'ID 2','Group1','Value 2'
UNION ALL SELECT 'ID 3','Group1','Value 3'
UNION ALL SELECT 'ID 4','Group1','Value 4'
UNION ALL SELECT 'ID 5','Group2','A Different Value 1'
UNION ALL SELECT 'ID 6','Group2','A Different Value 2'
UNION ALL SELECT 'ID 7','Group2','A Different Value 3';
SELECT ID, [Group], Val FROM #foo;
WITH x AS
(
SELECT
ID, [Group], Val,
rn = ROW_NUMBER() OVER (PARTITION BY [Group] ORDER BY val)
FROM #foo
)
UPDATE x
SET x.Val = y.Val
FROM x
INNER JOIN x AS y
ON x.[Group] = y.[Group]
WHERE y.rn = 1 AND x.rn > 1;
SELECT ID, [Group], Val FROM #foo;
Something like this maybe?
UPDATE table
SET table.valueColumn = CT.correctValueColumn
FROM table as CT
INNER JOIN table as IT on IT.group = CT.group AND CT.valueColumn LIKE '%:\%'
WHERE IT.valueColumn NOT LIKE '%:\%'
I don't have management studio on this machine so I'm not sure it's syntatically correct.
Hope this helps some.

how to make array_agg() work like group_concat() from mySQL

So I have this table:
create table test (
id integer,
rank integer,
image varchar(30)
);
Then some values:
id | rank | image
---+------+-------
1 | 2 | bbb
1 | 3 | ccc
1 | 1 | aaa
2 | 3 | c
2 | 1 | a
2 | 2 | b
I want to group them by id and concatenate the image name in the order given by rank. In mySQL I can do this:
select id,
group_concat( image order by rank asc separator ',' )
from test
group by id;
And the output would be:
1 aaa,bbb,ccc
2 a,b,c
Is there a way I can have this in postgresql?
If I try to use array_agg() the names will not show in the correct order and apparently I was not able to find a way to sort them. (I was using postgres 8.4 )
In PostgreSQL 8.4 you cannot explicitly order array_agg but you can work around it by ordering the rows passed into to the group/aggregate with a subquery:
SELECT id, array_to_string(array_agg(image), ',')
FROM (SELECT * FROM test ORDER BY id, rank) x
GROUP BY id;
In PostgreSQL 9.0 aggregate expressions can have an ORDER BY clause:
SELECT id, array_to_string(array_agg(image ORDER BY rank), ',')
FROM test
GROUP BY id;