I can't believe I can't figure this out or find anything related to this. I'm trying to generate a set of column headers dynamically but with no data (as if it was an empty table).
SELECT Null AS [CODE], Null AS [DESC];
will return
CODE DESC
----------- -----------
NULL NULL
which is close, but I need it to have no records:
CODE DESC
----------- -----------
As best I can replicate the exact requirement:
DECLARE #Table TABLE
(
[CODE] bit NULL,
[DESC] bit NULL
);
SELECT [CODE], [DESC]
FROM #Table;
Which is what I'll go with if I can't find anything similar but this just feels soooo verbose for something that feels trivial?
Just use a false condition in a WHERE clause:
SELECT Null AS [CODE], Null AS [DESC]
WHERE 1=0
See the demo.
This way you can pass any value to the 2 columns, not just null.
Try to use CTE
WITH CTE AS(
SELECT Null AS [CODE], Null AS [DESC]
)
SELECT * FROM CTE WHERE [CODE] IS NOT NULL AND [DESC] IS NOT NULL
SELECT null as [Code], null as [DESC]
WHERE 1=2
Related
An example scenario for my question would be:
How to get all persons who has multiple address types?
Now here's my sample data:
CREATE TABLE #tmp_1 (
ID uniqueidentifier PRIMARY KEY
, FirstName nvarchar(max)
, LastName nvarchar(max)
)
CREATE TABLE #tmp_2 (
SeedID uniqueidentifier PRIMARY KEY
, SomeIrrelevantCol nvarchar(max)
)
CREATE TABLE #tmp_3 (
KeyID uniqueidentifier PRIMARY KEY
, ID uniqueidentifier REFERENCES #tmp_1(ID)
, SeedID uniqueidentifier REFERENCES #tmp_2(SeedID)
, SomeIrrelevantCol nvarchar(max)
)
INSERT INTO #tmp_1
VALUES
('08781F73-A06B-4316-B6A5-802ED58E54BE', 'AAAAAAA', 'aaaaaaa'),
('4EC71FCE-997C-46AA-B119-6C5A2545DDC2', 'BBBBBBB', 'bbbbbbb'),
('B0726ABF-738E-48BC-95CB-091C9D731A0E', 'CCCCCCC', 'ccccccc'),
('6C6CE284-A63C-49D2-B2CC-F25C9CBC8FB8', 'DDDDDDD', 'ddddddd')
INSERT INTO #tmp_2
VALUES
('4D10B4EC-C929-4D6B-8C94-11B680CF2221', 'Value1'),
('4C891FE9-60B6-41BE-A64B-11A9A8B58AB2', 'Value2'),
('6F6EFED6-8EA0-4F70-A63F-6A103D0A71BD', 'Value3')
INSERT INTO #tmp_3
VALUES
(NEWID(), '08781F73-A06B-4316-B6A5-802ED58E54BE', '4D10B4EC-C929-4D6B-8C94-11B680CF2221', 'sdfsdgdfbgcv'),
(NEWID(), '08781F73-A06B-4316-B6A5-802ED58E54BE', '4C891FE9-60B6-41BE-A64B-11A9A8B58AB2', 'asdfadsas'),
(NEWID(), '08781F73-A06B-4316-B6A5-802ED58E54BE', '4C891FE9-60B6-41BE-A64B-11A9A8B58AB2', 'xxxxxeeeeee'),
(NEWID(), '4EC71FCE-997C-46AA-B119-6C5A2545DDC2', '4D10B4EC-C929-4D6B-8C94-11B680CF2221', 'sdfsdfsd'),
(NEWID(), 'B0726ABF-738E-48BC-95CB-091C9D731A0E', '4D10B4EC-C929-4D6B-8C94-11B680CF2221', 'zxczxcz'),
(NEWID(), 'B0726ABF-738E-48BC-95CB-091C9D731A0E', '6F6EFED6-8EA0-4F70-A63F-6A103D0A71BD', 'eerwerwe'),
(NEWID(), '6C6CE284-A63C-49D2-B2CC-F25C9CBC8FB8', '4D10B4EC-C929-4D6B-8C94-11B680CF2221', 'vbcvbcvbcv')
Which gives you:
This is my attempt:
SELECT
t1.*
, Cnt -- not really needed. Just added for visual purposes
FROM #tmp_1 t1
LEFT JOIN (
SELECT
xt.ID
, COUNT(1) Cnt
FROM (
SELECT
#tmp_3.ID
, COUNT(1) as Cnt
FROM #tmp_3
GROUP BY ID, SeedID
) xt
GROUP BY ID
) t2
ON t1.ID = t2.ID
WHERE t2.Cnt > 1
Which gives:
ID FirstName LastName Cnt
B0726ABF-738E-48BC-95CB-091C9D731A0E CCCCCCC ccccccc 2
08781F73-A06B-4316-B6A5-802ED58E54BE AAAAAAA aaaaaaa 2
Although this gives me the correct results, I'm afraid that this query is not the right way to do this performance-wise because of the inner queries. Any input is very much appreciated.
NOTE:
A person can have multiple address of the same address types.
"Person-Address" is not the exact use-case. This is just an example.
The Cnt column is not really needed in the result set.
The way you have named your sample tables and data help little in understanding the problem.
I think you want all IDs which have 2 or more SomeIrrelevantCol values in the last table?
This can be done by:
select * from #tmp_1
where ID in
(
select ID
from #tmp_3
group by ID
having count(distinct SomeIrrelevantCol)>=2
)
I have two tables, lets call them Table A and Table B. Both different sizes but have the same primary Key (ID). The variable field is (Name). I want to return rows from Table B that:
Data has changed
Data did not exist before
The returned data would have an additional column labelled comments with the value set to above each time the SQL executes. I have written the T-SQL below, however is there a better way to do this?
SELECT [ID]
,[Name]
,'Data did not exist before' AS [Comment]
FROM TABLENAMEB
WHERE [ID] NOT IN (SELECT [ID] FROM #TABLENAMEA)
UNION
SELECT B.[ID]
,B.[Name]
,'Data has changed' AS [Comment]
FROM TABLENAMEB B
LEFT JOIN TABLENAMEA A ON B.[ID] = A.[ID]
WHERE A.[Name] != B.[Name]
Something like this:
DECLARE #tblA TABLE(ID INT, Name VARCHAR(100));
INSERT INTO #tblA VALUES(1,'test1'),(2,'test2'),(4,'test4');
DECLARE #tblB TABLE(ID INT, Name VARCHAR(100));
INSERT INTO #tblB VALUES(2,'test2'),(3,'test3'),(4,'different');
SELECT CASE WHEN A.ID IS NULL THEN 'missing in A'
WHEN B.ID IS NULL THEN 'missing in B'
WHEN A.Name<>B.Name THEN 'different'
ELSE 'okay' END AS Comment
,*
FROM #tblA AS A
FULL OUTER JOIN #tblB AS B ON A.ID=B.ID
The result
Comment ID Name ID Name
missing in B 1 test1 NULL NULL
okay 2 test2 2 test2
different 4 test4 4 different
missing in A NULL NULL 3 test3
You could probably use left join and case to get the same result:
SELECT [ID]
,[Name]
,CASE
WHEN A.[Name] IS NULL THEN -- Assuming `Name` in table a is not nullable.
'Data did not exist before'
WHEN B.[Name] != A.[Name THEN
'Data has changed'
ELSE
''
END As [Comment]
FROM TABLENAMEB As B
LEFT JOIN #TABLENAMEA As A ON B.[ID] = A.[ID]
Edit: I just noticed you are using 2008. The features below will not be of any use.
You are writing this in T-SQL, thus you are using SQL Server. Look into either of these two features specifically designed to answer your question:
Change Data Capture
Temporal Tables
Aim to write a statement to produce a table with a numeric column that contains contra values, e.g.
Ref Value
a 100
b 75
c 50
c -50
b -75
a -100
I am new to SQL but aware it works row by row, so the only way I could think of doing this is write an initial SELECT statement into a temporary table and INSERT into my temporary table with the contra values, i.e.
SELECT
[Ref],
[Value]
INTO #Temp
FROM
mytable
INSERT INTO #Temp ([Ref], [Value])
SELECT
[Ref],
0 - [Value]
FROM
mytable
While this 'does the job' I fear it is 'messy' (could possibly cause problems when used for its intended purpose) and wondered if anyone would be able to provide a better solution.
Use Union ALL to combine the original and negative values. Then insert into temp table. Try this.
SELECT [Ref],
[Value]
INTO #Temp
FROM (SELECT [Ref],
[Value]
FROM mytable
UNION ALL
SELECT [Ref],
[Value] * -1
FROM mytable) a
If you just want to view the result remove the into #temp table
SELECT [Ref],
[Value]
FROM (SELECT [Ref],
[Value]
FROM mytable
UNION ALL
SELECT [Ref],
[Value] * -1
FROM mytable) a
I am trying to do the following but getting an "Invalid Column Name {column}" error. Can someone please help me see the error of my ways? We recently split a transaction table into 2 tables, one containing the often updated report column names and the other containing the unchanging transactions. This leave me trying to change what was a simple insert into 1 table to a complex insert into 2 tables with unique columns. I attempted to do that like so:
INSERT INTO dbo.ReportColumns
(
FullName
,Type
,Classification
)
OUTPUT INSERTED.Date, INSERTED.Amount, INSERTED.Id INTO dbo.Transactions
SELECT
[Date]
,Amount
,FullName
,Type
,Classification
FROM {multiple tables}
The "INSERTED.Date, INSERTED.Amount" are the source of the errors, with or without the "INSERTED." in front.
-----------------UPDATE------------------
Aaron was correct and it was impossible to manage with an insert but I was able to vastly improve the functionality of the insert and add some other business rules with the Merge functionality. My final solution resembles the following:
DECLARE #TransactionsTemp TABLE
(
[Date] DATE NOT NULL,
Amount MONEY NOT NULL,
ReportColumnsId INT NOT NULL
)
MERGE INTO dbo.ReportColumns AS Trgt
USING ( SELECT
{FK}
,[Date]
,Amount
,FullName
,Type
,Classification
FROM {multiple tables}) AS Src
ON Src.{FK} = Trgt.{FK}
WHEN MATCHED THEN
UPDATE SET
Trgt.FullName = Src.FullName,
Trgt.Type= Src.Type,
Trgt.Classification = Src.Classification
WHEN NOT MATCHED BY TARGET THEN
INSERT
(
FullName,
Type,
Classification
)
VALUES
(
Src.FullName,
Src.Type,
Src.Classification
)
OUTPUT Src.[Date], Src.Amount, INSERTED.Id INTO #TransactionsTemp;
MERGE INTO dbo.FinancialReport AS Trgt
USING (SELECT
[Date] ,
Amount ,
ReportColumnsId
FROM #TransactionsTemp) AS Src
ON Src.[Date] = Trgt.[Date] AND Src.ReportColumnsId = Trgt.ReportColumnsId
WHEN NOT MATCHED BY TARGET And Src.Amount <> 0 THEN
INSERT
(
[Date],
Amount,
ReportColumnsId
)
VALUES
(
Src.[Date],
Src.Amount,
Src.ReportColumnsId
)
WHEN MATCHED And Src.Amount <> 0 THEN
UPDATE SET Trgt.Amount = Src.Amount
WHEN MATCHED And Src.Amount = 0 THEN
DELETE;
Hope that helps someone else in the future. :)
Output clause will return values you are inserting into a table, you need multiple inserts, you can try something like following
declare #staging table (datecolumn date, amount decimal(18,2),
fullname varchar(50), type varchar(10),
Classification varchar(255));
INSERT INTO #staging
SELECT
[Date]
,Amount
,FullName
,Type
,Classification
FROM {multiple tables}
Declare #temp table (id int, fullname varchar(50), type varchar(10));
INSERT INTO dbo.ReportColumns
(
FullName
,Type
,Classification
)
OUTPUT INSERTED.id, INSERTED.fullname, INSERTED.type INTO #temp
SELECT
FullName
,Type
,Classification
FROM #stage
INSERT into dbo.transacrions (id, date, amount)
select t.id, s.datecolumn, s.amount from #temp t
inner join #stage s on t.fullname = s.fullname and t.type = s.type
I am fairly certain you will need to have two inserts (or create a view and use an instead of insert trigger). You can only use the OUTPUT clause to send variables or actual inserted values ti another table. You can't use it to split up a select into two destination tables during an insert.
If you provide more information (like how the table has been split up and how the rows are related) we can probably provide a more specific answer.
How would this INSERT statement be rewritten without the subquery, so it no longer results in the error "Subqueries are not allowed in this context. Only scalar expressions are allowed"?
INSERT INTO FUNCTIONAL_AREA (
FUNCTIONAL_AREA_UUID
,FUNCTIONAL_CATEGORY_UUID
,CREATE_DATETIME
,CREATE_USER
,LUPDATE_DATETIME
,LUPDATE_USER
,DESCRIPTION
,ITEM_CODE
,IS_ACTIVE
) VALUES (
NEWID()
,(select functional_category_uuid from functional_category where description = 'ADLs')
,GETDATE()
,'11111111-1111-1111-1111-111111111111'
,GETDATE()
,'11111111-1111-1111-1111-111111111111'
,'Bathing - Ability'
,1081
,1)
Thank you!
INSERT INTO FUNCTIONAL_AREA (
FUNCTIONAL_AREA_UUID
,FUNCTIONAL_CATEGORY_UUID
,CREATE_DATETIME
,CREATE_USER
,LUPDATE_DATETIME
,LUPDATE_USER
,DESCRIPTION
,ITEM_CODE
,IS_ACTIVE
)
select NEWID()
,functional_category_uuid
,GETDATE()
,'11111111-1111-1111-1111-111111111111'
,GETDATE()
,'11111111-1111-1111-1111-111111111111'
,'Bathing - Ability'
,1081
,1
from functional_category
where description = 'ADLs'
EDIT Adding:
If you only want one row inserted and you care about which row is selected from functional_category, then either narrow the where clause so only one row comes back, or use an order by and top 1:
INSERT INTO FUNCTIONAL_AREA (
FUNCTIONAL_AREA_UUID
,FUNCTIONAL_CATEGORY_UUID
,CREATE_DATETIME
,CREATE_USER
,LUPDATE_DATETIME
,LUPDATE_USER
,DESCRIPTION
,ITEM_CODE
,IS_ACTIVE
)
select top 1 NEWID()
,functional_category_uuid
,GETDATE()
,'11111111-1111-1111-1111-111111111111'
,GETDATE()
,'11111111-1111-1111-1111-111111111111'
,'Bathing - Ability'
,1081
,1
from functional_category
where description = 'ADLs'
order by <criteria that causes the correct row to be first>
The reason you are getting this error is because the VALUES clause restricts you to inserting one row. When you have a subquery as one of the columns then this could result in more than one value.
You could either do a 'select' insert like others have shown, or you could write the value to a variable and use the TOP 1 clause if the subquery returns more than one row.
Just a little correction if you want to insert exactly one row in all cases:
INSERT INTO FUNCTIONAL_AREA (
FUNCTIONAL_AREA_UUID,
FUNCTIONAL_CATEGORY_UUID,
CREATE_DATETIME,
CREATE_USER,
LUPDATE_DATETIME,
LUPDATE_USER,
DESCRIPTION,
ITEM_CODE,
IS_ACTIVE)
SELECT TOP (1) NEWID(),
functional_category_uuid,
GETDATE(),
'11111111-1111-1111-1111-111111111111',
GETDATE(),
'11111111-1111-1111-1111-111111111111',
'Bathing - Ability',
1081,
1
FROM functional_category
WHERE description = 'ADLs'
or
DECLARE #category_uuid uniqueidentifier
SELECT #category_uuid = functional_category_uuid
FROM functional_category
WHERE description = 'ADLs'
INSERT INTO FUNCTIONAL_AREA (
FUNCTIONAL_AREA_UUID,
FUNCTIONAL_CATEGORY_UUID,
CREATE_DATETIME,
CREATE_USER,
LUPDATE_DATETIME,
LUPDATE_USER,
DESCRIPTION,
ITEM_CODE,
IS_ACTIVE)
VALUES( NEWID(),
#category_uuid,
GETDATE(),
'11111111-1111-1111-1111-111111111111',
GETDATE(),
'11111111-1111-1111-1111-111111111111',
'Bathing - Ability',
1081,
1)