Simple query gone wrong - sql-server

I must be having an off day. This should be obvious but I don't get it.
-- check for necessary updates to dbnotes
select count(distinct table_name)
from ccsv4.[INFORMATION_SCHEMA].[COLUMNS]
returns 46
select count(distinct table_name)
from dbnotes
returns 44
select distinct table_name
from ccsv4.[INFORMATION_SCHEMA].[COLUMNS]
where table_name not in (select distinct table_name from dbnotes)
order by table_name
returns nothing
select distinct table_name
from dbnotes
where table_name not in (select distinct table_name
from ccsv4.[INFORMATION_SCHEMA].[COLUMNS])
order by table_name
returns nothing
What am I missing guys?

You are using not in. If any value from the subquery is NULL, nothing will be returned.
With a subquery, always use not exists. It has the right semantics:
select distinct table_name
from ccsv4.[INFORMATION_SCHEMA].[COLUMNS] c
where not exists (select 1
from dbnotes d
where d.table_name = c.table_name
);
I am pretty sure that tables have to have at least one column, so you might as well use information_schema.tables instead. It saves you the distinct:
select table_name
from ccsv4.information_schema.tables t
where not exists (select 1
from dbnotes d
where d.table_name = t.table_name
);

Related

Count columns of a result from with block?

I am exploring provided code. I have various CTEs, and I want to count the columns in each CTE? Or select the differences in columns per CTE?
The below code will grab the count from a table in the DB, but I'm not sure how to apply this to CTEs?
select count(*)
from INFORMATION_SCHEMA.columns
where TABLE_CATALOG = 'db_name'
and TABLE_NAME = 'table_name'
First Create CTE and then insert just one record into temp table , then query the sys.columns table to get the list of the columns in CTE
IF OBJECT_ID('tempdb.dbo.#temp', 'U') IS NOT NULL
DROP TABLE #temp;
go
With CTE1 as (select * from table_name)
select top 1 * into #temp from CTE1
SELECT count(*) FROM tempdb.sys.columns where object_id = object_id('tempdb..#temp')

T-SQL Merge from multiple source records

I've seen several similar questions but haven't found one that answers my question.
I have a source table with many individual notes for a company
CompanyName, Notes3
Company1, "spoke with someone"
Company2, "Email no longer works"
Company1, "Moved address"
I have a destination table (vchCompanyName is unique here)
vchCompanyName, vchNotes
Company1, "started business in 2005"
Company2, null
I want to end up with
vchCompanyName, vchNotes
Company1, "started business in 2005
spoke with someone
Moved address"
Company2, "Email no longer works"
I have tried this code
WITH CTE
AS (SELECT ROW_NUMBER() OVER (PARTITION BY CompanyName, Notes3 ORDER BY CompanyName) RowNum, *
FROM CompanyContact
)
merge dCompany as target
using CTE as source
on target.vchcompanyname = source.companyname
when matched and len(source.notes3)>0 and source.RowNum = 1
then
update set target.vchnote = vchnote + CHAR(13) + source.Notes3
But get the error
The MERGE statement attempted to UPDATE or DELETE the same row more than once. This happens when a target row matches more than one source row. A MERGE statement cannot UPDATE/DELETE the same row of the target table multiple times. Refine the ON clause to ensure a target row matches at most one source row, or use the GROUP BY clause to group the source rows.
Which is accurate.
I have also tried STRING_AGG but get an undefined UDF error.
How do I change my code to run iteratively?
--EDIT--
I had tried the following update code
WITH CTE
AS (SELECT ROW_NUMBER() OVER (PARTITION BY CompanyName, Notes3 ORDER BY CompanyName) RowNum, *
FROM CompanyContact
)
UPDATE dCompany SET vchNote = vchNote +
(select CHAR(13) + cc.Notes3 from CompanyContact cc
inner JOIN dCompany dc ON dc.vchCompanyName COLLATE database_default = LEFT(cc.CompanyName,50) COLLATE database_default
inner join CTE on dc.vchCompanyName COLLATE database_default = LEFT(CTE.CompanyName,50) COLLATE database_default
WHERE LEN(cc.Notes3)>0
and RowNum = 1
);
But get the error
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
#Chris Crawshaw, I will approach this by doing a 'union all' on the source and destination table to pick up all the notes for each company. Then using the STUFF function, it is easy to concatenate all the notes into one cell, while grouping by the induvidual company names. See the mockup below:
DECLARE #Source TABLE (CompanyName VARCHAR(20), Notes3 VARCHAR(50))
INSERT INTO #Source
SELECT 'Company1', 'spoke with someone' UNION ALL
SELECT 'Company2', 'Email no longer works' UNION ALL
SELECT 'Company1', 'Moved address'
DECLARE #Destination TABLE (vchCompanyName VARCHAR(20), vchNotes VARCHAR(500))
INSERT INTO #Destination
SELECT 'Company1', 'started business in 2005' UNION ALL
SELECT 'Company2', NULL
;WITH Temp AS (
SELECT *
FROM
(
SELECT *
FROM
#Destination D
WHERE D.vchNotes is not null
UNION ALL
SELECT *
FROM
#Source S
)h
)
update D
SET D.vchNotes=U.vchNotes
FROM #Destination D
LEFT JOIN(
SELECT t2.vchCompanyName, vchNotes=STUFF((
SELECT ',' + vchNotes
FROM Temp t1 where t1.vchCompanyName=t2.vchCompanyName
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
FROM
#Destination t2
GROUP BY
t2.vchCompanyName
)U ON
U.vchCompanyName=D.vchCompanyName
--TEST--
SELECT *
FROM
#Destination

SQL Server 2012 paging order by an unknown column

I'm trying to create a query that returns the paging of the rows from a generic table because the call that invokes this query will work on more tables and databases; the program where I'm working built the query in different parts of the code, and the only variable parameters that the database receive are #myTable, #offset and #row.
Initially, I tried a query for a single table like this
SELECT * FROM myTable ORDER BY myPKColumn
OFFSET #offset ROWS
FETCH NEXT #row ROWS ONLY;
and, with #offset = 0 and #row = 10, return me the first 10 rows ASC.
So far it works all without problem, but if I need to use this query for every table, I have problems to specify the column in the ORDER BY clause; to avoid this problem I've tried this query
SELECT * FROM #myTable ORDER BY (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME=#myTable and CONSTRAINT_NAME like 'PK_%')
OFFSET #offset ROWS
FETCH NEXT #row ROWS ONLY;
this because I know from the project structure that all the tables that I could invoke have a single primary key.
The query works, but the results are a bit strange: if I pass the same parameter of the precedent query, the result is the rows from 11 to 2 DESC.
Also, changing the offset value, I figured this strange relation:
If [ #offset <= ( 100 - #row ) ]
>
the results are the first (#row+1) DESC without the first row
( in this case all the rows between #row and #offset will never be retrieved)
Else
>
the result are the first #row starting from the (#offset + 1)
examples:
#offset = 90 and #row = 10 return rows from 13 to 2
#offset = 91 and #row = 10 return rows from 92 to 101
#offset = 101 and #row = 10 return rows from 102 to 111
I've tried some little variations like specify OBJECT_ID(...) or OBJECT_NAME(OBJECT_ID(...)) outside the second select, but without changes; also, I've tried to use the debug option, but I haven't the privileges to use it.
Any idea of what could cause this strange results?
P.S: sorry for my bad English; also it's my first post so, if I missed something I'll edit without problems
EDIT:
i found a way that logically should work, but it doesn't work:
i discovered that writing a query like
SELECT * FROM myTable ORDER BY 5 asc;
order my rows ascendent considering the values of the 5th column.
so i made the query:
select ORDINAL_POSITION from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'myTable' and COLUMN_NAME = (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME='myTable' and CONSTRAINT_NAME like 'PK_%')
that return me the position of the column with the primary key (unluckily for me, there are a lot of table where it isn't in the 1st position); the value that return is correct (i've verified a lot of table and it works always)
So, the next step, if i run the query
select * from mytable ORDER BY (select ORDINAL_POSITION from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'myTable' and COLUMN_NAME = (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME='myTable' and CONSTRAINT_NAME like 'PK_%' )) asc;
works with a little problem: i can declare asc or desc, but the result is always asc. (it's not a real problem, because like said before the order is not important im my case).
Now the real problem.
The last step is the query
select * from mytable ORDER BY (select ORDINAL_POSITION from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'myTable' and COLUMN_NAME = (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME='myTable' and CONSTRAINT_NAME like 'PK_%' )) asc
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY;
and here nothing has changed from the problem before the edit: the result are the rows from 11 to 2 desc.
to avoid the possibility that the error could be a string interpretation of the number returned, i've add a cast to int
SELECT * FROM myTable
ORDER BY CAST((select ORDINAL_POSITION from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'myTable' and COLUMN_NAME = (SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_NAME='myTable' and CONSTRAINT_NAME like 'PK_%' ))AS int) asc
OFFSET 0 ROWS
FETCH NEXT 10 ROWS ONLY;
but nothing changed.
It seems that the new query works fine until the offset/fetch is added.

How to select all non unique columns from my DB?

I'm learning SQL utilizing the common Microsoft AdventureWorks2014 sample database in SQL Server 2014.
I just learned about HAVING and Information Schema today and am trying to combine the two.
Reason being, I'd really like to quickly tell which columns from all tables are shared. This works:
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
ORDER BY COLUMN_NAME, TABLE_SCHEMA
But... the output gives me unique column names that only slows me down.
I've tried applying answers from "How to select non 'unique' rows" (among 5-7 other SOF pages!) such as:
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, COUNT(*)
FROM INFORMATION_SCHEMA.COLUMNS
GROUP BY COLUMN_NAME
HAVING COUNT(COLUMN_NAME) > 1
...but I get this error:
Msg 8120, Level 16, State 1, Line 1 Column
'information_schema.columns.TABLE_SCHEMA' is invalid in the select
list because it is not contained in either an aggregate function or
the GROUP BY clause.
You can use your query, to retrive all the columns that are shared, and then join to the original table for all the information (schema, name):
SELECT t.TABLE_SCHEMA,
t.table_name,
t.column_name
FROM INFORMATION_SCHEMA.COLUMNS t
INNER JOIN (
SELECT s.column_name
FROM INFORMATION_SCHEMA.COLUMNS s
GROUP BY s.column_name
HAVING COUNT(s.column_name) > 1
) tt ON (t.column_name = tt.column_name)
SELECT *
FROM (
SELECT
col = c.name,
obj_name = o.name,
sch_name = SCHEMA_NAME(o.[schema_id]),
col_type = TYPE_NAME(c.system_type_id),
RowNum = COUNT(1) OVER (PARTITION BY c.name, o.[type] ORDER BY 1/0)
FROM sys.columns c
JOIN sys.objects o ON c.[object_id] = o.[object_id]
WHERE o.[type] = 'U'
) t
WHERE t.RowNum > 1
ORDER BY t.col
Output:
col obj_name sch_name col_type
----------------------- ------------------- --------- ------------
dbid spt_fallback_usg dbo smallint
dbid spt_fallback_db dbo smallint
xserver_name spt_fallback_usg dbo varchar
xserver_name spt_fallback_db dbo varchar
xserver_name spt_fallback_dev dbo varchar

Is there a way to get the fields names of a table created in a function / stored procedure?

when I need the columns of an existing table I use the query:
SELECT c.[name]
FROM
(SELECT * from syscolumns) c
INNER JOIN
(SELECT [id] from sysobjects where name= 'tableName') o on c.[id]=o.[id]
I need the fields of a table that I create during runTime:
select
a.ID,
b.lName,
b.fName
into #T
from
a
inner join
b on a.id=b.id
.
select * from #T_columns
will result a table with 3 rows:
id
lName
fName
How can I do it?
Thanks
When you create a temp table, it will be in tempdb. You can look it up like this:
SELECT COLUMN_NAME
FROM tempdb.INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME LIKE '#T|_%' ESCAPE '|'
If you do a SELECT * FROM INFORMATION_SCHEMA.TABLES in tempdb, you'll see the temp table name you use (#T) actually has a number of underscores appended to it followed by a unique identifier. So you won't find it it you just search where table_name = '#T'.
So that's why you have to use a LIKE as I've shown above. This will match on "#T_" followed by any other other characters.
Try this
SELECT sc.NAME
FROM
tempdb..SYSOBJECTS so JOIN
tempdb..SYSCOLUMNS sc ON sc.id = so.id
WHERE so.NAME LIKE '#T%'

Resources