Suggestions on T-SQL query [duplicate] - sql-server

Assuming I have a table containing the following information:
FK | Field1 | Field2
=====================
3 | ABC | *NULL*
3 | *NULL* | DEF
is there a way I can perform a select on the table to get the following
FK | Field1 | Field2
=====================
3 | ABC | DEF
Thanks
Edit: Fix field2 name for clarity

Aggregate functions may help you out here. Aggregate functions ignore NULLs (at least that's true on SQL Server, Oracle, and Jet/Access), so you could use a query like this (tested on SQL Server Express 2008 R2):
SELECT
FK,
MAX(Field1) AS Field1,
MAX(Field2) AS Field2
FROM
table1
GROUP BY
FK;
I used MAX, but any aggregate which picks one value from among the GROUP BY rows should work.
Test data:
CREATE TABLE table1 (FK int, Field1 varchar(10), Field2 varchar(10));
INSERT INTO table1 VALUES (3, 'ABC', NULL);
INSERT INTO table1 VALUES (3, NULL, 'DEF');
INSERT INTO table1 VALUES (4, 'GHI', NULL);
INSERT INTO table1 VALUES (4, 'JKL', 'MNO');
INSERT INTO table1 VALUES (4, NULL, 'PQR');
Results:
FK Field1 Field2
-- ------ ------
3 ABC DEF
4 JKL PQR

There are a few ways depending on some data rules that you have not included, but here is one way using what you gave.
SELECT
t1.Field1,
t2.Field2
FROM Table1 t1
LEFT JOIN Table1 t2 ON t1.FK = t2.FK AND t2.Field1 IS NULL
Another way:
SELECT
t1.Field1,
(SELECT Field2 FROM Table2 t2 WHERE t2.FK = t1.FK AND Field1 IS NULL) AS Field2
FROM Table1 t1

There might be neater methods, but the following could be one approach:
SELECT t.fk,
(
SELECT t1.Field1
FROM `table` t1
WHERE t1.fk = t.fk AND t1.Field1 IS NOT NULL
LIMIT 1
) Field1,
(
SELECT t2.Field2
FROM `table` t2
WHERE t2.fk = t.fk AND t2.Field2 IS NOT NULL
LIMIT 1
) Field2
FROM `table` t
WHERE t.fk = 3
GROUP BY t.fk;
Test Case:
CREATE TABLE `table` (fk int, Field1 varchar(10), Field2 varchar(10));
INSERT INTO `table` VALUES (3, 'ABC', NULL);
INSERT INTO `table` VALUES (3, NULL, 'DEF');
INSERT INTO `table` VALUES (4, 'GHI', NULL);
INSERT INTO `table` VALUES (4, NULL, 'JKL');
INSERT INTO `table` VALUES (5, NULL, 'MNO');
Result:
+------+--------+--------+
| fk | Field1 | Field2 |
+------+--------+--------+
| 3 | ABC | DEF |
+------+--------+--------+
1 row in set (0.01 sec)
Running the same query without the WHERE t.fk = 3 clause, it would return the following result-set:
+------+--------+--------+
| fk | Field1 | Field2 |
+------+--------+--------+
| 3 | ABC | DEF |
| 4 | GHI | JKL |
| 5 | NULL | MNO |
+------+--------+--------+
3 rows in set (0.01 sec)

I had a similar problem. The difference was that I needed far more control over what I was returning so I ended up with an simple clear but rather long query. Here is a simplified version of it based on your example.
select main.id, Field1_Q.Field1, Field2_Q.Field2
from
(
select distinct id
from Table1
)as main
left outer join (
select id, max(Field1)
from Table1
where Field1 is not null
group by id
) as Field1_Q on main.id = Field1_Q.id
left outer join (
select id, max(Field2)
from Table1
where Field2 is not null
group by id
) as Field2_Q on main.id = Field2_Q.id
;
The trick here is that the first select 'main' selects the rows to display. Then you have one select per field. What is being joined on should be all of the same values returned by the 'main' query.
Be warned, those other queries need to return only one row per id or you will be ignoring data

if one row has value in field1 column and other rows have null value then this Query might work.
SELECT
FK,
MAX(Field1) as Field1,
MAX(Field2) as Field2
FROM
(
select FK,ISNULL(Field1,'') as Field1,ISNULL(Field2,'') as Field2 from table1
)
tbl
GROUP BY FK

My case is I have a table like this
---------------------------------------------
|company_name|company_ID|CA | WA |
---------------------------------------------
|Costco | 1 |NULL | 2 |
---------------------------------------------
|Costco | 1 |3 |Null |
---------------------------------------------
And I want it to be like below:
---------------------------------------------
|company_name|company_ID|CA | WA |
---------------------------------------------
|Costco | 1 |3 | 2 |
---------------------------------------------
Most code is almost the same:
SELECT
FK,
MAX(CA) AS CA,
MAX(WA) AS WA
FROM
table1
GROUP BY company_name,company_ID
The only difference is the group by, if you put two column names into it, you can group them in pairs.

SELECT Q.FK
,ISNULL(T1.Field1, T2.Field2) AS Field
FROM (SELECT FK FROM Table1
UNION
SELECT FK FROM Table2) AS Q
LEFT JOIN Table1 AS T1 ON T1.FK = Q.FK
LEFT JOIN Table2 AS T2 ON T2.FK = Q.FK
If there is one table, write Table1 instead of Table2

Related

How to get data as a list in SQL server [duplicate]

Let's suppose I have a temporary table which looks like this:
+----+------+
| Id | Value|
+----+------+
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
+----+------+
And I want my table to be like this:
+----+----------+
| Id | ValueList|
+----+----------+
| 1 | 1,2,3 |
| 2 | 1,2 |
+----+----------+
So basically I need to group my values as a comma separated list.
I already tried the following:
SELECT Id, STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #MyTable FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable
GROUP BY Id
But I get something like:
+----+---------------------+
| Id | ValueList |
+----+---------------------+
| 1 | 1,1,1,1,1,1,... |
+----+---------------------+
I cant find what I am doing wrong. Could someone help with this query? Or point me to a right direction?
Thank you.
You are missing the condition inside the sub query.
SELECT t2.Id, STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #MyTable t1 where t1.Id =t2.ID FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable t2
GROUP BY t2.Id
Demo
One alternative to using GROUP BY on the Id would be to use select distinct:
SELECT DISTINCT
Id,
STUFF((SELECT ',' + CAST(t2.VALUE AS varchar)
FROM #MyTable t2
WHERE t2.Id = t1.Id
FOR XML PATH('')), 1 ,1, '') AS ValueList
FROM #MyTable t1
Demo
One can also combine a FOR XML with a CROSS APPLY (or an OUTER APPLY) for this.
Example snippet:
declare #T table (id int, value int);
insert into #T values (1,1),(1,2),(1,3),(2,1),(2,2);
select id, stuff(x.list,1,1,'') as list
from (select distinct id from #T) as t
cross apply (
select concat(',',t2.value)
from #T t2
where t2.id = t.id
for xml path('')
) x(list)
order by id;
Result:
id list
-- -----
1 1,2,3
2 1,2
And starting from MS Sql Server 2017, STRING_AGG can be used instead.
select id, string_agg(value,',') as list
from Yourtable t
group by id;
Try this :
create table #t(id int, value int)
insert into #t values
(1,1),
(1,2),
(1,3),
(2,1),
(2,2)
SELECT t2.Id,
STUFF((SELECT ',' + CAST(VALUE AS varchar) FROM #t t1 where t1.Id =t2.ID FOR XML PATH('')), 1 ,1, '') AS list
FROM #t t2
GROUP BY t2.Id
output :
Id list
--- -------
1 1,2,3
2 1,2
Simple Solution
SELECT Id, GROUP_CONCAT(Value) as ValueList FROM MyTable GROUP BY Id;
add distinct to values if required
SELECT Id, GROUP_CONCAT(DISTINCT Value) as ValueList FROM MyTable GROUP BY Id;

How to show a specific view from a table in MS-SQL

I'm new to database concepts. So, i need some help in solving a particular problem.
Say i have a table named emp whose data are as below:
id | dept | doj
100 | FS | 02-04-13
100 | HST | 02-04-14
100 | ETA | 02-04-15
What i want to display is:
id | from | to | doj
100 | FS | HST | 02-04-14
100 | HST | ETA | 02-04-15
Any help would be appreciated. Thank you.
The database i'm using is MS-SQL server 13v.
You can use apply :
select t.id, t.dept as [from], t1.dept as [to], t1.doj
from table t cross apply
(select top (1) t1.*
from table t1
where t1.id = t.id and t1.doj > t.doj
order by t1.doj
) t1
With a self join to the table:
select t.id, tt.dept [from], t.dept [to], t.doj
from tablename t inner join tablename tt
on tt.id = t.id and
tt.doj = (select max(doj) from tablename where id = t.id and doj < t.doj)
See the demo
If I understand your question correctly and you want to get data from current and subsequent row in the same result set, you may use LEAD() function:
Input:
CREATE TABLE #Table (
id int,
dept varchar(10),
doj date
)
INSERT INTO #Table
(id, dept, doj)
VALUES
(100, 'FS', '2013-04-02'),
(100, 'HST', '2014-04-02'),
(100, 'ETA', '2015-04-02'),
(101, 'XTA', '2015-04-02'),
(101, 'YTA', '2015-04-02')
Statement:
SELECT *
FROM
(
SELECT
id,
dept [from],
LEAD(dept) OVER (PARTITION BY id ORDER BY id, doj) AS [to],
LEAD(doj) OVER (PARTITION BY id ORDER BY id, doj) AS [doj]
FROM #Table
) t
WHERE t.[to] IS NOT NULL
Output:
id from to doj
100 FS HST 2014-04-02
100 HST ETA 2015-04-02
101 XTA YTA 2015-04-02

Update a table using temp table with select statement for multiple rows

I have temp table in my SP and a table in my DB and I need to update the table in the DB, and as of now I am able to update the table using select statement.
But I am having multiple records in my temp table and I am able to update only my last row of the table in DB.
Below is the query I am having,
UPDATE
Table_A
SET
Table_A.col2 = Table_B.col2,
Table_A.col3 = Table_B.col3
FROM
Some_Table AS Table_A
INNER JOIN temp_Table AS Table_B ON Table_A.col1 = Table_B.col1
And DB table structures
col1 | col2 | col3
1 | India | Delhi
2 | US | NewYork
3 | UK | London
And temp table structure as below
col1 | col2 | col3
1 | US | NewYork
2 | UK | London
3 | India | Delhi
So, I need to update my table for multiple rows.
As far as I am understanding your post I think this should be the solution for that which will update properly. Hope this helps
WITH CTE1
AS ( SELECT Col1 ,
Col2 ,
ROW_NUMBER() OVER ( PARTITION BY COl1 ORDER BY Col2 ) AS rn
FROM table_1 t1
),
CTE2
AS ( SELECT Col1 ,
Col2 ,
ROW_NUMBER() OVER ( PARTITION BY COl1 ORDER BY Col2 ) AS rn
FROM table_2 t2
)
UPDATE br
SET ....
FROM Cte1 c1
INNER JOIN cte2 c2 br ON c1.Col1 = c2.Col1
AND c1.rn = c2.rn;

How to make query to count values from two tables

I have three tables main 'maintable' table and two sub tables 'table1' and 'table2' the main table 'maintable' contains tow columns 'ID' and 'name' like that:
ID name
.... ......
1 Khalid
2 Jone
3 Steve
and the first sub table 'table1' contains 't1ID' and 'column' and 'ID' (foreign key) from 'maintable' like that:
t1ID column ID
...... ....... ....
1 Value 1
2 Value 1
3 Value 1
4 Value 2
and the second sub table 'table2' contains 't2ID' and 'column' and 'ID' (foreign key) from 'maintable' like that:
t2ID column ID
...... ....... ....
1 Value 2
2 Value 1
3 Value 1
4 Value 3
I want to make query to find count of (table1.ID) as A and count of (table2.ID) as B like that:
name A B
...... ... ...
khalid 3 2
Jone 1 1
Steve 0 1
Try this :
select name,
(select count(t1.ID) from table1 t1 where t1.ID = main.ID) as A,
(select count(t2.ID) from table2 t2 where t2.ID = main.ID) as B
from maintable main
Try this out:
;with cte1 as (
SELECT ID, COUNT(1) AS Cnt
FROM table1
GROUP BY ID
), cte2 as (
SELECT ID, COUNT(1) AS Cnt
FROM table2
GROUP BY ID
)
SELECT m.name, ISNULL(cte1.Cnt, 0) AS A, ISNULL(cte2.Cnt, 0) AS B
FROM maintable m
LEFT JOIN cte1 ON cte1.ID = m.ID
LEFT JOIN cte2 ON cte2.ID = m.ID
It can also be done with subqueries, but I like CTEs more (query is more readable).
Try this:
with t0_t1 as (
select
t.id,
t.nm,
count(t1.id) as A
from table0 t
left join table1 t1 on t.id = t1.id
group by t.id, t.nm
)
select t.nm, t.A, count(t2.id) as B
from t0_t1 t
left join table2 t2 on t.id = t2.id
group by t.nm, t.A
Example: http://sqlfiddle.com/#!6/341ff/10
create table table0 (id int, nm varchar(20));
insert into table0 values (1,'Khalid'),(2,'Jone'),(3,'Steve');
create table table1 (t1id int, col varchar(20), id int);
insert into table1 values
(1, 'v', 1), (2, 'v', 1), (3, 'v', 1), (4, 'v', 2);
create table table2 (t2id int, col varchar(20), id int);
insert into table2 values
(1, 'v', 2), (2, 'v', 1), (3, 'v', 1), (4, 'v', 3);
Result:
| nm | A | B |
|--------|---|---|
| Steve | 0 | 1 |
| Jone | 1 | 1 |
| Khalid | 3 | 2 |

sql - how to query member of group with recursion (1 table with user and groups)

Following structure exists:
CREATE TABLE rel(
entry_id int,
parent_id int
)
CREATE TABLE entries(
entry_id int,
name varchar(44)
)
Following data exists:
INSERT INTO entries VALUES (1,'user 1');
INSERT INTO entries VALUES (2,'group 2');
INSERT INTO entries VALUES (3,'group 3');
INSERT INTO entries VALUES (4,'user 4');
INSERT INTO entries VALUES (5,'user 5');
INSERT INTO rel VALUES (3,2);
INSERT INTO rel VALUES (4,2);
INSERT INTO rel VALUES (1,3);
INSERT INTO rel VALUES (5,3);
INSERT INTO rel VALUES (2,NULL);
Result should look like:
group_id| group_name | member_id | member_name | level
2 | group 2 | 4 | user 4 | 0
2 | group 2 | 1 | user 1 | 1
2 | group 2 | 5 | user 5 | 1
3 | group 3 | 1 | user 1 | 0
3 | group 3 | 5 | user 5 | 0
I already tried stuff like the following but it's not returning the results I need:
SELECT
entries.entry_id,
entries.name,
rel.parent_id,
(SELECT name FROM entries WHERE entry_id=parent_id) AS parent_name
INTO
#tmpEntries
FROM
entries, rel
WHERE
rel.entry_id = entries.entry_id
;
SELECT * FROM #tmpEntries;
WITH MyCTE
AS (
SELECT
entry_id,
name,
parent_id,
--CAST('' AS VARCHAR(44)) AS
parent_name
FROM #tmpEntries
--WHERE parent_id IS NULL
UNION ALL
SELECT
#tmpEntries.entry_id,
#tmpEntries.name,
#tmpEntries.parent_id,
--MyCTE.name AS
#tmpEntries.parent_name
FROM #tmpEntries
INNER JOIN MyCTE ON #tmpEntries.parent_id = MyCTE.entry_id
--WHERE #tmpEntries.parent_id IS NOT NULL
-- WHERE NOT EXISTS (SELECT entry_id FROM rel WHERE parent_id=#tmpEntries.entry_id)
)
SELECT DISTINCT *
FROM MyCTE
ORDER BY parent_id
;
WITH MyCTE2
AS (
SELECT
entry_id,
name,
parent_id,
--CAST('' AS VARCHAR(44)) AS
parent_name
FROM #tmpEntries
--WHERE parent_id IS NULL
UNION ALL
SELECT
#tmpEntries.entry_id,
#tmpEntries.name,
#tmpEntries.parent_id,
--MyCTE.name AS
#tmpEntries.parent_name
FROM #tmpEntries
INNER JOIN MyCTE2 ON #tmpEntries.parent_id = MyCTE2.entry_id
--WHERE #tmpEntries.parent_id IS NOT NULL
WHERE NOT EXISTS (SELECT entry_id FROM rel WHERE parent_id=#tmpEntries.entry_id)
)
SELECT DISTINCT *
FROM MyCTE2
ORDER BY parent_id
This will work under assumption that group is anything that contains one or more members i.e. empty group will be considered a simple member.
with cte_hierarchy as
(
select entry_id, parent_id, 0 as level
from rel
where parent_id is not null
union all
select h.entry_id, rel.parent_id, h.level + 1 as level
from cte_hierarchy h
inner join rel on h.parent_id = rel.entry_id
)
select
g.entry_id as group_id,
g.name as group_name,
e.entry_id as member_id,
e.name as member_name,
h.level
from cte_hierarchy h
inner join entries e on e.entry_id = h.entry_id
inner join entries g on g.entry_id = h.parent_id
where not exists (select * from rel where parent_id = h.entry_id)
order by g.entry_id, h.level, e.entry_id
The where clause excludes nested groups that would otherwise appear as members.

Resources