copy column values to another part of the table - sql

I have a SELECT statement along with a join that uses two tables. We have tables Shell and table Tanker.
It looks something like this select S.*, T.* from shell S left outer join tanker T on S.id = T.id. Note that Tanker has about 183 fields.
So I get the following data
====Shell table==== =========Tanker table columsn======================
orgid org eli lang orgid org Lang {Other columns start}
906875 s 1 1 NULL NULL NULL NULL NULL NULL NULL
906876 s 2 2 NULL NULL NULL NULL NULL NULL NULL
906877 b 2 1 NULL NULL NULL NULL NULL NULL NULL
906878 s 1 1 NULL NULL NULL NULL NULL NULL NULL
906879 b 2 1 NULL NULL NULL NULL NULL NULL NULL
Now I want to select orgid, org, lang, and the other 183 fields. So right now I do
select T.* FROM ....
but the problem with this is that when it selects the orgid, org, lang, and eli it gets the NULLS only. Which makes sense because I am only selecting T.* and its an left join so naturally these will be null.
However, for my purpose I need them to be the values from the shell table. Ie, I want to select the orgid, org, eli, lang from Shell and then the rest from Tanker. I know the one way I do this is the following
select s.orgid, s.org, s.eli, s.lang, t.column, t.column2, t.column3
BUT RECALL that Tanker T has almost 200 columns. I do not want to write 200 columns on my script.
So here is the question.
Is there any way I can "copy" the values of orgid, org, eli, lang (ie, the actual not null values) to the columns which are null? That way I can just do select T.* and select all 200 columns.
so really if you are confused the end result I am looking for is
====Shell table==== =========Tanker table columsn======================
orgid org eli lang orgid org Eli Lang {Other columns start}
906875 s 1 1 906875 s 1 1 NULL NULL
906876 s 2 2 906876 s 2 2 NULL NULL NULL
906877 b 2 1 906877 b 2 1 NULL NULL
906878 s 1 1 906878 s 1 1 NULL NULL NULL
906879 b 2 1 906879 b 2 1 NULL NULL NULL

In this case, I would recommend just writing a view that would perform this select and you can reuse it wherever you need this kind of functionality. Should you ever need to modify it, it's a simple matter of updating the view (although you'll have to be careful you don't have any fundamental logic working against that view).
In order to do this easily, you can actually click-drag a table from SSMS into the query window. Just click-drag the Columns folder under the table in question into the query window and it will copy ALL the columns into it. That way you can do the select manually without having to type everything in.

you can use Isnull (SQL server). It allows you to provide a value to use incase column you are selecting is null.
select isnull(t.org, s.org) as org
if T has a value, it gives you that, else it give you the one from S
EDIT:
SPFiredrake pointed I focused on the wrong part. Another thing you could do (if all your worried about is a HUGE SQL script) is to select * from T and then just the few columns from S that may be needed and then handle the logic of comparrision in whatever lanaguage will be playing with your data.

Related

Hue is not capturing null values

i am trying to do a count on null values however , it is not able to count null values.
example of table :
Country Id Id_typ Info
Us 123 NULL Testing
Us 124 NULL Testing
Us 125 Bob testing
this is my script to count null values
select count(id_typ) from combined_a where id_typ= 'NULL' limit 1
i have tried
select count(id_typ) from table_a where id_typ is null limit 1
however when i have change the condition to search id_typ = bob, it was able to do a count . i am unsure on what did i do wrong , any advice?
You need is null and count(*):
select count(*)
from table_a
where id_typ is null;
limit 1 is redundant. A SQL query with an aggregation function and no group by always returns one row.

How to insert values to table based on column value

I have a table called BranchServices and its structure as follows,
FkBranchId FkServiceId SortValue
6 1 1
7 1 1
8 1 NULL
6 2 2
7 2 2
8 2 NULL
6 3 3
Each branch have its own Sort order (SortValue).Some branch haven't sort order and that branch SortValues are NULL.When I create a new service I insert values to this table through the cross join as follows,
INSERT INTO BranchServices
SELECT b.BranchId,
s.ServiceID,
NULL
FROM #insertedServiceID s
CROSS JOIN Branch b WHERE b.IsActive = 1
But I need to change above query.While inserting newly created service id to each branch, I need to check its current sort order. If that branch consists with SortValue = NULL then I need to insert SortValue as Null null and if that branch already have Sort order need to insert 0 to sortValue how to check that branch have sorting order or not before inserting newly created service. can I check it inside the cross join? so then how?
Use a subquery to get the SortValue for the FkBranchId that you insert:
INSERT INTO BranchServices
SELECT b.BranchId,
s.ServiceID,
CASE WHEN(SELECT MIN(SortValue) FROM BranchServices WHERE FkBranchId = b.BranchId) IS NOT NULL THEN 0 END
FROM #insertedServiceID s
CROSS JOIN Branch b WHERE b.IsActive = 1
I used MIN() to make sure that the subquery will return only 1 row (MAX() would also work).

How to Deduplicate an id column by adding an overflow column?

Alright guys I need some help! I am need to duplicate the ID Column and I'm having trouble with adding a column without loosing important data.
Is there a way to make an "overflow column" that would take the secondary [tags] and put them into a new column?
Here is the example:
**UniqueId** **Age** **Zip*** **Tag**
1 20 11111 yellow
2 25 33333 blue
2 25 33333 black
3 30 44444 purple
3 30 44444 pink
3 30 44444 white
This is what i want the output to look like
**UniqueId** **Age** **Zip*** **Tag1** **Tag2** **Tag3**
1 20 11111 yellow NULL NULL
2 25 33333 blue black NULL
3 30 44444 purple pink white
Your help would be greatly appreciated!!!
If you now the maximum number of tags, you can use pivot or conditional aggregation:
select t.uniqueid, t.age, t.zip,
max(case when seqnum = 1 then tag end) as tag_1,
max(case when seqnum = 2 then tag end) as tag_2,
max(case when seqnum = 3 then tag end) as tag_2
from (select t.*,
row_number() over (partition by uniqueid order by (select null)) as seqnum
from t
) t
group by t.uniqueid, t.age, t.zip;
Though I tend to prefer conditional aggregations as Gordon illustrated... they offer a bit more flexibilty.
You can do a simple PIVOT
Example
Select *
From (
Select UniqueID
,Age
,Zip
,Tag
,Col = concat('Tag',Row_Number() over (Partition By UniqueId order by Tag) )
From YourTable
) A
Pivot (Max(Tag) for Col in ([Tag1],[Tag2],[Tag3],[Tag4]) ) p
Returns
UniqueID Age Zip Tag1 Tag2 Tag3 Tag4
1 20 11111 yellow NULL NULL NULL
2 25 33333 black blue NULL NULL
3 30 44444 pink purple white NULL
attention: Do not store the age as an int number! Rather store a DOB and compute the age...
This is not a real answer to your question, but the thing you should rather do:
To be honest: Your question can be solved (and there are good answers already), but you should not do this.
Whenever you feel the need to add numbers to a field's name (Tag1, Tag2...) the design will be wrong (almost ever). Push these values into a related side table (just the Id and the tag), remove the column from your original table and place a foreign key pointing to the new table. Now you can join these values whenever you need them. PIVOT (or conditional aggregation) is for output only...
This is completely untested, so be careful with your data (backup!), but something along these lines should work:
CREATE TABLE TagTable (ID INT IDENTITY
,FKOriginal INT NOT NULL CONSTRAINT FK_TagTable_OriginalTable FOREIGN KEY REFERENCES OriginalTable(UniqueId)
,Tag VARCHAR(100) NOT NULL);
--an index to support the fk
CREATE NONCLUSTERED INDEX IX_TagTable_FKOriginal ON TagTable(FKOriginal);
GO
--shift the existing data
INSERT INTO TagTable --you might use DISTINCT...
SELECT UniqueId,Tag
FROM OriginalTable;
GO
--delete duplicated rows
WITH cte AS
(
SELECT *
,ROW_NUMBER() OVER(PARTITION BY UniqueId ORDER BY UniqueId) AS RowId --Find a better sort column if needed
FROM OriginalTable
)
DELETE FROM cte
WHERE RowId>1; --Only the first remains
GO
--throw away the tag column in the original table
ALTER TABLE OriginalTable DROP COLUMN Tag;
GO
--See the result via JOIN-Select
SELECT *
FROM OriginalTable AS o
INNER JOIN TagTable AS t ON o.UniqueId=t.FKOriginal;
If you need these pivoted columns, you can use the approaches provided in other answers with the final SELECT too.

Results of PIVOT in 0/1 true/false or other binary format

I have a pivot query which returns how much each customer has spent per category returning following result:
transaction_id 1 2 3 4 5 6 7
10-52927 NULL NULL NULL NULL NULL NULL NULL
10-52928 NULL NULL NULL NULL NULL NULL NULL
10-52929 8 NULL NULL NULL NULL 149 NULL
10-52930 NULL NULL NULL NULL NULL NULL NULL
10-52931 NULL NULL NULL NULL NULL NULL NULL
10-52932 NULL NULL 12 NULL NULL NULL NULL
10-52934 NULL NULL NULL NULL NULL NULL NULL
10-52935 NULL NULL NULL NULL NULL 33 NULL
10-52936 NULL NULL NULL NULL NULL NULL NULL
Pivot is based on multiple lines aggregating sales from product level to categories, meaning that each transaction_id record is aggregated from typically 20+ lines using sum() as pivot parameter.
What I would like to achieve is to get results in 0-1 format indicating whether customer has purchased anything from category 1,2,3,4,5... ~400 without creating extra tables since the operation has to be repeated for 100+ stores.
Any suggestion how to perform this task without creating additional tables to store and update results?
Thanks in advance.
EDIT:
The query producting presented output is following:
select transaction_id, [1], [2], [3], // up to ~400 numerical categories
from (SELECT [transaction_id]
,[category]
,sum([SUM]) as SUM_2
FROM [XXX].[dbo].[receipts]
left join // join of recept table with category table
where // store type related parametrs
group by transaction_id, category) p
PIVOT (sum([sum_2]) for [category] in
([1], [2], [3], [4], ... ) // shortened list of categories in order to improve code readability
) as pvt
ORDER BY pvt.transaction_id
categories
OK you can do this without changing you cte (the FROM part).
In between SELECT and FROM add
INTO #MyTempTable
Safer is creating this before hand, checking to see if it already exists, and INSERTing from your existing SELECT.
Then you can just do:
SELECT transaction_id, CASE WHEN [1] IS NULL THEN 0 ELSE 1 END [1],
-- repeat each case
FROM #MyTempTable
If you can changed your CTE, remove the group by and the aggregate [SUM_v2], and just give it a value of 1 always. In the PIVOT change SUM to MIN
Then the columns from the table can be checked in a more simple fashion: ISNULL([1], 0) [1]
This is actually a pretty fun question. This is a second solution where I get a cross product of the group and spreading column IDs so that there is always a value. I can imagine there is a performance penalty associated with this method.
I used #Mark Bannister's use of SIGN to produce the 1 in the 0/1 column.
;WITH P AS
(
SELECT
IDS.transaction_id, IDS.category, CAST(ISNULL(SIGN(C.Sum), 0) AS INT) [hassum]
FROM
(SELECT DISTINCT
A.transaction_id, B.category
FROM Reciepts [A] CROSS JOIN Categories [B]) AS IDS
LEFT JOIN Transaction AS T ON T.transaction_id = IDS.transaction_ID
LEFT JOIN Category AS C ON -- some unspecified join ...
AND C.Category = IDS.Category
)
SELECT transaction_id, [1], [2], [3]
FROM P
PIVOT (MIN(hassum) FOR category IN ([1], [2], [3]) ) AS [hassum]
If you can group your source table so there will be 0 or 1 matching row, then using COUNT(Category) should work. (It works in my similar but slightly different scenario)

Keep one instance of duplicate appearing in one of two columns

I've got a table containing one column with unique ID and one column with each unique ID's spouse ID (if they have a spouse). The problem is that each spouse ID also appears in the unique ID column, so when I pull a list, attempting to treat a couple as a single unit, I'm often doublecounting for a single couple.
What's a good, efficient way of taking a given list of unique IDs, checking to see if their spouse is also in the same list of unique IDs, and returning only one unique ID per couple?
The issue is a little more complicated in that sometimes both spouses are not included in the same list, so it's not simply a matter of keeping one person if they're married. In the event that the spouse isn't also in the same list, I want to make sure to retain the one that is. I also want to make sure I'm retaining all people who have a NULL value in the spouse ID column.
Subset of table in question:
Unique_ID Spouse_ID
1 2
2 1
3 NULL
4 NULL
5 10
6 25
7 NULL
8 9
9 8
10 5
In this excerpt, ID's 3, 4, and 7 are all single. ID's 1, 2, 5, 8, and 9 have spouses that appear in the Unique_ID column. ID 6 has a spouse whose ID does not appear in the Unique_ID column. So, I'd want to keep ID's 1 (or 2), 3, 4, 5 (or 10), 6, 7, and 8 (or 9). Hope that makes sense.
My inclination would be to combine the two lists and remove duplicates:
select distinct id
from ((select id
from t
) union all
(select spouse_id
from t
where spouse_id in (select id from t)
)
) t
But, your question asked for an efficient way. Another way to think about this is to add a new column which is the spouse id if in the id list or NULL otherwise (this uses a left outer join. Then there are three cases:
There is no spouse id, so use the id
The id is less than the original id. Use it.
The spouse id is less than the original id. Discard this record, because the original is being used.
Here is an explicit way of expressing this:
select IdToUse
from (select t.*, tspouse.id tsid,
(case when tspouse.id is null then t.id
when t.id < tspouse.id then t.id
else NULL
end) as IdToUse
from t left outer join
t tspouse
on t.spouse_id = tspouse.id
) t
where IdToUse is not null;
You can simplify this to:
select t.*, tspouse.id tsid,
(case when tspouse.id is null then t.id
when t.id < tspouse.id then t.id
else NULL
end) as IdToUse
from t left outer join
t tspouse
on t.spouse_id = tspouse.id
where tspouse.id is null or
t.id < tspouse.id
Two tables is just plain bad design
Combine the tables
select id
from table
where id < spouseID
or spouseID is null