Summarizing count of multiple talbes in one row or column - sql-server

I've designed a migration script and as the last sequence, I'm running the following two lines.
select count(*) from Origin
select count(*) from Destination
However, I'd like to present those numbers as cells in the same table. I haven't decided yet if it's most suitable to put them as separate rows in one column or adjacent columns on one row but I do want them in the same table.
How can I select stuff from those selects into vertical/horizontal line-up?
I've tried select on them both with and without parentheses but id didn't work out (probably because of the missing from)...
This questions is related to another one but differs in two aspects. Firstly, it's much more to-the-point and clearer states the issue. Secondly, it asks about both horizontal and vertical line-up of the selected values whereas the linked questions only regards the former.
select
select count(*) from Origin,
select count(*) from Destination
select(
select count(*) from Origin,
select count(*) from Destination)

You need to nest the two select statements under a main (top) SELECT in order to get one row with the counts of both tables:
SELECT
(select count(*) from Origin) AS OriginCount,
(select count(*) from Destination) AS DestinationCount
SQLFiddle for the above query
I hope this is what you are looking for, since the "same table" you are mentioning is slightly confusing. (I'm assuming you're referring to result set)
Alternatively you can use UNION ALL to return two cells with the count of both tables.
SELECT COUNT(*), 'Origin' 'Table' FROM ORIGIN
UNION ALL
SELECT COUNT(*), 'Destination' 'Table' FROM Destination
SQLFiddle with UNION ALL
SQLFiddle with UNION
I recommend adding the second text column so that you know the corresponding table for each number.
As opposed to simple UNION the UNION ALL command will return two rows everytime. The UNION command will generate a single result (single cell) if the count of rows in both tables is the same (the same number).

...or if you want vertical...
select 'OriginalCount' as Type, count(*)
from origin
union
select 'DestinationCount' as Type, count(*)
from destination

Related

SQL Server : concatenate results of 2 queries as a string

I am new to SQL, and I need to make a string out of the UNION result. I have seen many similar questions but they were either related to concatenating results of a single SQL query, or they were using JOIN on some kind of row id, while I do not need this and do not have any column on which I can use JOIN.
I have the following UNION:
(
SELECT COUNT(*)
FROM [db].[table1]
WHERE [ItemType] = 2
)
UNION
(
SELECT TOP(1) Items
FROM [db].[table2]
WHERE [ItemType] = 2
)
It returns a simple result with two rows:
15
10
15 is the total number of items, and 10 is the number of items left available.
I want to return a table with only one entry 10/15. What is the simplest way to achieve it? Thanks.
A bit of a guess, but perhaps:
SELECT CONCAT((SELECT COUNT(*) FROM [db].[table1] WHERE [ItemType] = 2)),'/',
(SELECT TOP(1) Items FROM [db].[table2] WHERE [ItemType] = 2 ORDER BY {Your Column}));
Note the {Your Column} which you need to replace with an appropriate column to give the correct consistent result. Having a TOP without an ORDER BY can (and will) produce inconsistent results, as tables in SQL Server are stored in unordered heaps; therefore the "TOP 1" will be whatever row SQL Server "finds" (retrieves) first from your table and is effectively random.
You have lurking problems in what you are trying to do.
First, UNION is a distincting operation. if you happen to have 15 total items AND 15 available, you'll only get one row back. That's not what you want. (UNION ALL would fix this, but you don't need UNION stuff at all).
Your next issue(?) may be your data model choice. The second table (Items) has values in it - you are pulling one row out of a particular type, but you don't have any control over which one gets pulled out. It could be any value in the set. If you want the count of items available, as opposed to the first item randomly that SQL picks for you, then you may want to adjust your query. (For "give me the first item and it actually is the count", you would add an ORDER BY into that subselect to help SQL pick the proper "first" order in a given sort. For "I want the count of distinct items in this table, you might want count(*) or count(distinct item) depending on your semantic).
Once you sort these two things out, you can then use two subselects to get each scalar value and then convert them to strings as you are attempting to do in your example. Here's an example on how the pattern to do this should look once you clarify your data model issue
select convert(nvarchar(100),a) + '/' + convert(nvarchar(100),b) FROM
(
select (select count(*) from sys.objects) as a, (select count(*) from sys.objects) as b
) C
Result:
101/101
It's a fairly quick operation so for readability I'd try something like this. You can format your results easily at the bottom too, should you need to add dashes or extra spaces too.
DECLARE #A int,
#B int
SET #A = (
SELECT COUNT(*)
FROM [db].[table1]
WHERE [ItemType] = 2
)
SET #B = (
SELECT TOP(1) Items
FROM [db].[table2]
WHERE [ItemType] = 2
)
SELECT (#A + '/' + #B) as result

Using Top in T-SQL

A question on using Top. For example, we have this SQL statement:
SELECT TOP (5) WITH TIES orderid, orderdate, custid, empid
FROM Sales.Orders
ORDER BY orderdate DESC;
It orders return rows by orderdate first then select the top most five rows.
But isn't that ORDER clause happens after SELECT clause, which means that the first five order in random will be returned first then those five rows are ordered by orderdate?
The order of commands in the statement doesn't reflect the actual order of operations that SQL follows. See this article which shows the order to be:
from
where
group by
having
select
order by
limit
As you can see, the TOP operation (limit) is the last to be executed.
Question has already an accepted answer. But I would like to quote content from Microsoft Documentation.
Logical Processing Order of the SELECT statement
FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP
But isn't that ORDER clause happens after SELECT clause, which means
that the first five order in random will be returned first then those
five rows are ordered by orderdate ?
No. ORDER BY is processed after the SELECT, but limiting the result set to 5 rows happens even later.
The physical details of actual query processing may vary, but the end result would be as if the server sorted the whole table by orderdate, then picked the top 5 (or more if needed due to ties) rows, return those rows and discard the rest.

Collate data from different Databases into same output window

I need to display data in different columns in the same output window from multiple different database sources. It would be ok to output this data to a file if necessary. For example say I have the following script that I need run on databases with identical schema:
SELECT TOP 3 item_id, COUNT(*) as itemcount_db1
FROM DB1.dbo.table
GROUP BY item_id ORDER BY itemcount_db1
SELECT TOP 3 item_id, COUNT(*) as itemcount_bd2
FROM DB2.dbo.table
GROUP BY item_id ORDER BY itemcount_bd2
So that the output would not be in two sequential and separate windows (as I hundreds of DBs and want to do a single copy and paste). I'm happy to create all of the individual scripts to get the data, just need to combine them somehow.
For one, you can use sp_MSforeachdb or a potential better one by Aaron Bertrand so you don't have to copy and paste all the scripts. I'm not sure you'd want the results going horizontally here, but instead just create a column with the DB flag. Here is a way using UNION and a CTE (since you need the order by for TOP).
with db1 as(
SELECT TOP 3
item_id,
COUNT(*) as itemcount
,'DB1'
FROM
DB1.dbo.table
GROUP BY
item_id
ORDER BY
itemcount_bd2)
db2 as(
SELECT TOP 3
item_id,
COUNT(*) as itemcount
,'DB2'
FROM
DB2.dbo.table
GROUP BY
item_id
ORDER BY
itemcount_bd2)
select * from db1
union all
select * from db2

Select one column multiple time

I want to select one column two time from a table.
E.g
( Select rent as rent1, rent as rent2 From Expense)
But I don't know how I can Select this column multiple time as each has its own Where Clause.
Means I want to select One Column two time On two different condition.
Use a union
select test, 'rent1' from tableA where condA
union
select test, 'rent2' from tableA where condB
If you would want to have both values in one result line (can't think of a reason why you would want this, but anyway...) you could use something like a self-join:
select a.rent as rent1, b.rent as rent2
from Expense a,
Expense b
where a.condition
and b.condition
and a/b-join-condition

SQL WHERE NOT EXISTS (skip duplicates)

Hello I'm struggling to get the query below right. What I want is to return rows with unique names and surnames. What I get is all rows with duplicates
This is my sql
DECLARE #tmp AS TABLE (Name VARCHAR(100), Surname VARCHAR(100))
INSERT INTO #tmp
SELECT CustomerName,CustomerSurname FROM Customers
WHERE
NOT EXISTS
(SELECT Name,Surname
FROM #tmp
WHERE Name=CustomerName
AND ID Surname=CustomerSurname
GROUP BY Name,Surname )
Please can someone point me in the right direction here.
//Desperate (I tried without GROUP BY as well but get same result)
DISTINCT would do the trick.
SELECT DISTINCT CustomerName, CustomerSurname
FROM Customers
Demo
If you only want the records that really don't have duplicates (as opposed to getting duplicates represented as a single record) you could use GROUP BY and HAVING:
SELECT CustomerName, CustomerSurname
FROM Customers
GROUP BY CustomerName, CustomerSurname
HAVING COUNT(*) = 1
Demo
First, I thought that #David answer is what you want. But rereading your comments, perhaps you want all combinations of Names and Surnames:
SELECT n.CustomerName, s.CustomerSurname
FROM
( SELECT DISTINCT CustomerName
FROM Customers
) AS n
CROSS JOIN
( SELECT DISTINCT CustomerSurname
FROM Customers
) AS s ;
Are you doing that while your #Tmp table is still empty?
If so: your entire "select" is fully evaluated before the "insert" statement, it doesn't do "run the query and add one row, insert the row, run the query and get another row, insert the row, etc."
If you want to insert unique Customers only, use that same "Customer" table in your not exists clause
SELECT c.CustomerName,c.CustomerSurname FROM Customers c
WHERE
NOT EXISTS
(SELECT 1
FROM Customers c1
WHERE c.CustomerName = c1.CustomerName
AND c.CustomerSurname = c1.CustomerSurname
AND c.Id <> c1.Id)
If you want to insert a unique set of customers, use "distinct"
Typically, if you're doing a WHERE NOT EXISTS or WHERE EXISTS, or WHERE NOT IN subquery,
you should use what is called a "correlated subquery", as in ypercube's answer above, where table aliases are used for both inside and outside tables (where inside table is joined to outside table). ypercube gave a good example.
And often, NOT EXISTS is preferred over NOT IN (unless the WHERE NOT IN is selecting from a totally unrelated table that you can't join on.)
Sometimes if you're tempted to do a WHERE EXISTS (SELECT from a small table with no duplicate values in column), you could also do the same thing by joining the main query with that table on the column you want in the EXISTS. Not always the best or safest solution, might make query slower if there are many rows in that table and could cause many duplicate rows if there are dup values for that column in the joined table -- in which case you'd have to add DISTINCT to the main query, which causes it to SORT the data on all columns.
-- Not efficient at all.
And, similarly, the WHERE NOT IN or NOT EXISTS correlated subqueries can be accomplished (and give the exact same execution plan) if you LEFT OUTER JOIN the table you were going to subquery -- and add a WHERE . IS NULL.
You have to be careful using that, but you don't need a DISTINCT. Frankly, I prefer to use the WHERE NOT IN subqueries or NOT EXISTS correlated subqueries, because the syntax makes the intention clear and it's hard to go wrong.
And you do not need a DISTINCT in the SELECT inside such subqueries (correlated or not). It would be a waste of processing (and for WHERE EXISTS or WHERE IN subqueries, the SQL optimizer would ignore it anyway and just use the first value that matched for each row in the outer query). (Hope that makes sense.)

Resources