Archive for January, 2015

SQL fejtörő 6. megoldás

Wednesday, January 28th, 2015

Ebben a feladatban meg kellett határozni azokat a sorokat, amelyek összege kiad egy sort egy másik táblában. Mivel nem lehet tudni, hány sort kell szummázni, ezért kombinációkat kellett képezni az elemekből.
A feladat nehezebb része tehát, hogyan kell SQL-ből kombinációkat képezni?

;WITH Combinations AS
(
	SELECT  [Level]=1,
			CombinationId = CONVERT(VARCHAR(MAX),id)+',',
			Szamlaszam,
			TotalPrice = CONVERT(DECIMAL(10,2), Ar),
			LastId = Id
	FROM  Szamlatetel

	UNION ALL

	SELECT  [Level]=[Level]+1,
			CombinationId = CombinationId + CONVERT(VARCHAR(3),id) + ',',
			c.Szamlaszam,
			TotalPrice = CONVERT(DECIMAL(10,2),TotalPrice + Ar),
			LastId = Id
	FROM  Combinations c  
	INNER JOIN Szamlatetel i 
		    ON  i.Szamlaszam = c.Szamlaszam AND i.ID > LastId 
)
select * from Combinations;

Ennek kimenete:

1	1,	Szamla1	20.00	1
1	2,	Szamla1	111.00	2
1	3,	Szamla1	250.00	3
1	4,	Szamla1	20.00	4
1	5,	Szamla1	15.00	5
1	6,	Szamla1	189.00	6
1	7,	Szamla1	100.00	7
1	8,	Szamla1	95.00	8
1	9,	Szamla2	1000.00	9
1	10,	Szamla2	3000.00	10
2	9,10,	Szamla2	4000.00	10
2	7,8,	Szamla1	195.00	8
2	6,7,	Szamla1	289.00	7
2	6,8,	Szamla1	284.00	8
3	6,7,8,	Szamla1	384.00	8
2	5,6,	Szamla1	204.00	6
2	5,7,	Szamla1	115.00	7
2	5,8,	Szamla1	110.00	8
3	5,7,8,	Szamla1	210.00	8
3	5,6,7,	Szamla1	304.00	7
3	5,6,8,	Szamla1	299.00	8
4	5,6,7,8,	Szamla1	399.00	8
2	4,5,	Szamla1	35.00	5
2	4,6,	Szamla1	209.00	6
2	4,7,	Szamla1	120.00	7
2	4,8,	Szamla1	115.00	8
3	4,7,8,	Szamla1	215.00	8
3	4,6,7,	Szamla1	309.00	7
3	4,6,8,	Szamla1	304.00	8
4	4,6,7,8,	Szamla1	404.00	8
3	4,5,6,	Szamla1	224.00	6
3	4,5,7,	Szamla1	135.00	7
3	4,5,8,	Szamla1	130.00	8
4	4,5,7,8,	Szamla1	230.00	8
4	4,5,6,7,	Szamla1	324.00	7
4	4,5,6,8,	Szamla1	319.00	8
5	4,5,6,7,8,	Szamla1	419.00	8
2	3,4,	Szamla1	270.00	4
2	3,5,	Szamla1	265.00	5
2	3,6,	Szamla1	439.00	6
2	3,7,	Szamla1	350.00	7
2	3,8,	Szamla1	345.00	8
3	3,7,8,	Szamla1	445.00	8
3	3,6,7,	Szamla1	539.00	7
3	3,6,8,	Szamla1	534.00	8
4	3,6,7,8,	Szamla1	634.00	8
3	3,5,6,	Szamla1	454.00	6
3	3,5,7,	Szamla1	365.00	7
3	3,5,8,	Szamla1	360.00	8
4	3,5,7,8,	Szamla1	460.00	8
4	3,5,6,7,	Szamla1	554.00	7
4	3,5,6,8,	Szamla1	549.00	8
5	3,5,6,7,8,	Szamla1	649.00	8
3	3,4,5,	Szamla1	285.00	5
3	3,4,6,	Szamla1	459.00	6
3	3,4,7,	Szamla1	370.00	7
3	3,4,8,	Szamla1	365.00	8
4	3,4,7,8,	Szamla1	465.00	8
4	3,4,6,7,	Szamla1	559.00	7
4	3,4,6,8,	Szamla1	554.00	8
5	3,4,6,7,8,	Szamla1	654.00	8
4	3,4,5,6,	Szamla1	474.00	6
4	3,4,5,7,	Szamla1	385.00	7
4	3,4,5,8,	Szamla1	380.00	8
5	3,4,5,7,8,	Szamla1	480.00	8
5	3,4,5,6,7,	Szamla1	574.00	7
5	3,4,5,6,8,	Szamla1	569.00	8
6	3,4,5,6,7,8,	Szamla1	669.00	8
2	2,3,	Szamla1	361.00	3
2	2,4,	Szamla1	131.00	4
2	2,5,	Szamla1	126.00	5
2	2,6,	Szamla1	300.00	6
2	2,7,	Szamla1	211.00	7
2	2,8,	Szamla1	206.00	8
3	2,7,8,	Szamla1	306.00	8
3	2,6,7,	Szamla1	400.00	7
3	2,6,8,	Szamla1	395.00	8
4	2,6,7,8,	Szamla1	495.00	8
3	2,5,6,	Szamla1	315.00	6
3	2,5,7,	Szamla1	226.00	7
3	2,5,8,	Szamla1	221.00	8
4	2,5,7,8,	Szamla1	321.00	8
4	2,5,6,7,	Szamla1	415.00	7
4	2,5,6,8,	Szamla1	410.00	8
5	2,5,6,7,8,	Szamla1	510.00	8
3	2,4,5,	Szamla1	146.00	5
3	2,4,6,	Szamla1	320.00	6
3	2,4,7,	Szamla1	231.00	7
3	2,4,8,	Szamla1	226.00	8
4	2,4,7,8,	Szamla1	326.00	8
4	2,4,6,7,	Szamla1	420.00	7
4	2,4,6,8,	Szamla1	415.00	8
5	2,4,6,7,8,	Szamla1	515.00	8
4	2,4,5,6,	Szamla1	335.00	6
4	2,4,5,7,	Szamla1	246.00	7
4	2,4,5,8,	Szamla1	241.00	8
5	2,4,5,7,8,	Szamla1	341.00	8
5	2,4,5,6,7,	Szamla1	435.00	7
5	2,4,5,6,8,	Szamla1	430.00	8
6	2,4,5,6,7,8,	Szamla1	530.00	8
3	2,3,4,	Szamla1	381.00	4
3	2,3,5,	Szamla1	376.00	5
3	2,3,6,	Szamla1	550.00	6
3	2,3,7,	Szamla1	461.00	7
3	2,3,8,	Szamla1	456.00	8
4	2,3,7,8,	Szamla1	556.00	8
4	2,3,6,7,	Szamla1	650.00	7
4	2,3,6,8,	Szamla1	645.00	8
5	2,3,6,7,8,	Szamla1	745.00	8
4	2,3,5,6,	Szamla1	565.00	6
4	2,3,5,7,	Szamla1	476.00	7
4	2,3,5,8,	Szamla1	471.00	8
5	2,3,5,7,8,	Szamla1	571.00	8
5	2,3,5,6,7,	Szamla1	665.00	7
5	2,3,5,6,8,	Szamla1	660.00	8
6	2,3,5,6,7,8,	Szamla1	760.00	8
4	2,3,4,5,	Szamla1	396.00	5
4	2,3,4,6,	Szamla1	570.00	6
4	2,3,4,7,	Szamla1	481.00	7
4	2,3,4,8,	Szamla1	476.00	8
5	2,3,4,7,8,	Szamla1	576.00	8
5	2,3,4,6,7,	Szamla1	670.00	7
5	2,3,4,6,8,	Szamla1	665.00	8
6	2,3,4,6,7,8,	Szamla1	765.00	8
5	2,3,4,5,6,	Szamla1	585.00	6
5	2,3,4,5,7,	Szamla1	496.00	7
5	2,3,4,5,8,	Szamla1	491.00	8
6	2,3,4,5,7,8,	Szamla1	591.00	8
6	2,3,4,5,6,7,	Szamla1	685.00	7
6	2,3,4,5,6,8,	Szamla1	680.00	8
7	2,3,4,5,6,7,8,	Szamla1	780.00	8
2	1,2,	Szamla1	131.00	2
2	1,3,	Szamla1	270.00	3
2	1,4,	Szamla1	40.00	4
2	1,5,	Szamla1	35.00	5
2	1,6,	Szamla1	209.00	6
2	1,7,	Szamla1	120.00	7
2	1,8,	Szamla1	115.00	8
3	1,7,8,	Szamla1	215.00	8
3	1,6,7,	Szamla1	309.00	7
3	1,6,8,	Szamla1	304.00	8
4	1,6,7,8,	Szamla1	404.00	8
3	1,5,6,	Szamla1	224.00	6
3	1,5,7,	Szamla1	135.00	7
3	1,5,8,	Szamla1	130.00	8
4	1,5,7,8,	Szamla1	230.00	8
4	1,5,6,7,	Szamla1	324.00	7
4	1,5,6,8,	Szamla1	319.00	8
5	1,5,6,7,8,	Szamla1	419.00	8
3	1,4,5,	Szamla1	55.00	5
3	1,4,6,	Szamla1	229.00	6
3	1,4,7,	Szamla1	140.00	7
3	1,4,8,	Szamla1	135.00	8
4	1,4,7,8,	Szamla1	235.00	8
4	1,4,6,7,	Szamla1	329.00	7
4	1,4,6,8,	Szamla1	324.00	8
5	1,4,6,7,8,	Szamla1	424.00	8
4	1,4,5,6,	Szamla1	244.00	6
4	1,4,5,7,	Szamla1	155.00	7
4	1,4,5,8,	Szamla1	150.00	8
5	1,4,5,7,8,	Szamla1	250.00	8
5	1,4,5,6,7,	Szamla1	344.00	7
5	1,4,5,6,8,	Szamla1	339.00	8
6	1,4,5,6,7,8,	Szamla1	439.00	8
3	1,3,4,	Szamla1	290.00	4
3	1,3,5,	Szamla1	285.00	5
3	1,3,6,	Szamla1	459.00	6
3	1,3,7,	Szamla1	370.00	7
3	1,3,8,	Szamla1	365.00	8
4	1,3,7,8,	Szamla1	465.00	8
4	1,3,6,7,	Szamla1	559.00	7
4	1,3,6,8,	Szamla1	554.00	8
5	1,3,6,7,8,	Szamla1	654.00	8
4	1,3,5,6,	Szamla1	474.00	6
4	1,3,5,7,	Szamla1	385.00	7
4	1,3,5,8,	Szamla1	380.00	8
5	1,3,5,7,8,	Szamla1	480.00	8
5	1,3,5,6,7,	Szamla1	574.00	7
5	1,3,5,6,8,	Szamla1	569.00	8
6	1,3,5,6,7,8,	Szamla1	669.00	8
4	1,3,4,5,	Szamla1	305.00	5
4	1,3,4,6,	Szamla1	479.00	6
4	1,3,4,7,	Szamla1	390.00	7
4	1,3,4,8,	Szamla1	385.00	8
5	1,3,4,7,8,	Szamla1	485.00	8
5	1,3,4,6,7,	Szamla1	579.00	7
5	1,3,4,6,8,	Szamla1	574.00	8
6	1,3,4,6,7,8,	Szamla1	674.00	8
5	1,3,4,5,6,	Szamla1	494.00	6
5	1,3,4,5,7,	Szamla1	405.00	7
5	1,3,4,5,8,	Szamla1	400.00	8
6	1,3,4,5,7,8,	Szamla1	500.00	8
6	1,3,4,5,6,7,	Szamla1	594.00	7
6	1,3,4,5,6,8,	Szamla1	589.00	8
7	1,3,4,5,6,7,8,	Szamla1	689.00	8
3	1,2,3,	Szamla1	381.00	3
3	1,2,4,	Szamla1	151.00	4
3	1,2,5,	Szamla1	146.00	5
3	1,2,6,	Szamla1	320.00	6
3	1,2,7,	Szamla1	231.00	7
3	1,2,8,	Szamla1	226.00	8
4	1,2,7,8,	Szamla1	326.00	8
4	1,2,6,7,	Szamla1	420.00	7
4	1,2,6,8,	Szamla1	415.00	8
5	1,2,6,7,8,	Szamla1	515.00	8
4	1,2,5,6,	Szamla1	335.00	6
4	1,2,5,7,	Szamla1	246.00	7
4	1,2,5,8,	Szamla1	241.00	8
5	1,2,5,7,8,	Szamla1	341.00	8
5	1,2,5,6,7,	Szamla1	435.00	7
5	1,2,5,6,8,	Szamla1	430.00	8
6	1,2,5,6,7,8,	Szamla1	530.00	8
4	1,2,4,5,	Szamla1	166.00	5
4	1,2,4,6,	Szamla1	340.00	6
4	1,2,4,7,	Szamla1	251.00	7
4	1,2,4,8,	Szamla1	246.00	8
5	1,2,4,7,8,	Szamla1	346.00	8
5	1,2,4,6,7,	Szamla1	440.00	7
5	1,2,4,6,8,	Szamla1	435.00	8
6	1,2,4,6,7,8,	Szamla1	535.00	8
5	1,2,4,5,6,	Szamla1	355.00	6
5	1,2,4,5,7,	Szamla1	266.00	7
5	1,2,4,5,8,	Szamla1	261.00	8
6	1,2,4,5,7,8,	Szamla1	361.00	8
6	1,2,4,5,6,7,	Szamla1	455.00	7
6	1,2,4,5,6,8,	Szamla1	450.00	8
7	1,2,4,5,6,7,8,	Szamla1	550.00	8
4	1,2,3,4,	Szamla1	401.00	4
4	1,2,3,5,	Szamla1	396.00	5
4	1,2,3,6,	Szamla1	570.00	6
4	1,2,3,7,	Szamla1	481.00	7
4	1,2,3,8,	Szamla1	476.00	8
5	1,2,3,7,8,	Szamla1	576.00	8
5	1,2,3,6,7,	Szamla1	670.00	7
5	1,2,3,6,8,	Szamla1	665.00	8
6	1,2,3,6,7,8,	Szamla1	765.00	8
5	1,2,3,5,6,	Szamla1	585.00	6
5	1,2,3,5,7,	Szamla1	496.00	7
5	1,2,3,5,8,	Szamla1	491.00	8
6	1,2,3,5,7,8,	Szamla1	591.00	8
6	1,2,3,5,6,7,	Szamla1	685.00	7
6	1,2,3,5,6,8,	Szamla1	680.00	8
7	1,2,3,5,6,7,8,	Szamla1	780.00	8
5	1,2,3,4,5,	Szamla1	416.00	5
5	1,2,3,4,6,	Szamla1	590.00	6
5	1,2,3,4,7,	Szamla1	501.00	7
5	1,2,3,4,8,	Szamla1	496.00	8
6	1,2,3,4,7,8,	Szamla1	596.00	8
6	1,2,3,4,6,7,	Szamla1	690.00	7
6	1,2,3,4,6,8,	Szamla1	685.00	8
7	1,2,3,4,6,7,8,	Szamla1	785.00	8
6	1,2,3,4,5,6,	Szamla1	605.00	6
6	1,2,3,4,5,7,	Szamla1	516.00	7
6	1,2,3,4,5,8,	Szamla1	511.00	8
7	1,2,3,4,5,7,8,	Szamla1	611.00	8
7	1,2,3,4,5,6,7,	Szamla1	705.00	7
7	1,2,3,4,5,6,8,	Szamla1	700.00	8
8	1,2,3,4,5,6,7,8,	Szamla1	800.00	8

Mivel minden sor egy kombinációt kell leírjon, kénytelenek vagyunk valahogy tömöríteni sok értéket egy oszlopba, ezért ez a varchar(max)-os pakolás.
A második rész már egyszerűbb, a kombinációk mindegyikére kiszámolt összeget kell összeveti a fej táblával:

;WITH Combinations AS
(
	SELECT  [Level]=1,
			CombinationId = CONVERT(VARCHAR(MAX),id)+',',
			Szamlaszam,
			TotalPrice = CONVERT(DECIMAL(10,2), Ar),
			LastId = Id
	FROM  Szamlatetel

	UNION ALL

	SELECT  [Level]=[Level]+1,
			CombinationId = CombinationId + CONVERT(VARCHAR(3),id) + ',',
			c.Szamlaszam,
			TotalPrice = CONVERT(DECIMAL(10,2),TotalPrice + Ar),
			LastId = Id
	FROM  Combinations c  
	INNER JOIN Szamlatetel i 
		    ON  i.Szamlaszam = c.Szamlaszam AND i.ID > LastId 
)
SELECT i.id, i.Szamlaszam, i.Ar, s.Ado 
FROM Combinations c
INNER JOIN Szamlafej s
	    ON s.OsszAr = c.TotalPrice
INNER JOIN Szamlatetel i 
	    ON CombinationId LIKE '%' + CONVERT(VARCHAR(3),i.id) + ',%'
ORDER BY s.Ado, s.Szamlaszam, s.ID;

Két megoldás is érkezett, mindkettő jó, gratulálok a megfejtőknek, ez tényleg nehéz feladat volt.

Az egyik megoldás hasonlít az enyémre:

;WITH CTE as
( 
	SELECT OsszAr, Ado, t.ID, Ar, cast(Ar as decimal(18,2)) as CSum,
	right('0' + CAST(t.Id as Varchar(max)),2) as path
	from Szamlatetel t inner join Szamlafej f on t.Szamlaszam=f.Szamlaszam

	UNION all

	SELECT OsszAr,
	Ado,
	Szamlatetel.ID,
	Szamlatetel.Ar,
	cast(Szamlatetel.Ar+CTE.CSum as decimal(18,2)) as CSum,
	CTE.path+','+ right('0' + CAST(Szamlatetel.Id as Varchar(max)),2) as path
	from Szamlatetel
	JOIN CTE on Szamlatetel.Ar+CTE.CSum<=OsszAr and CTE.ID<Szamlatetel.ID
)
select Szamlatetel.ID, Szamlaszam, Szamlatetel.Ar, Ado
from Szamlatetel, CTE
where CTE.path like '%' + right('0' + CAST(Szamlatetel.ID as Varchar(max)),2) + '%' and CTE.CSum=OsszAr
order by Ado, Szamlaszam, Szamlatetel.ID

A másik megoldás is hasonló, de ebben a kombinációkat a szerző tábla típusú változóban tárolja átmenetileg:

declare @Osszeg decimal(18,2), @SzamlaTetelId int

declare @Kombinacio table
(
	Id int identity(1,1) not null primary key,
	ElozoId int null,
	OsszAr decimal(18,2) not null,
	SzamlaTetelId int not null
)

set @SzamlaTetelId = 0

while 1 = 1
begin
	select top 1 @Osszeg = T.Ar, @SzamlaTetelId = T.ID
	from
	Szamlatetel T
	where
	T.ID > @SzamlaTetelId order by T.ID

	if @@rowcount = 0
	break

	insert into @Kombinacio(ElozoId, OsszAr, SzamlaTetelId)
	select S.Id, S.OsszAr + @Osszeg, @SzamlaTetelId
	from
	(select OsszAr, Id from @Kombinacio union select 0, null) S
	where
	S.OsszAr + @Osszeg not in (select OsszAr from @Kombinacio)
end

;with Elozmeny as
(
	select
	K.Id, K.ElozoId, K.SzamlaTetelId, K.OsszAr
	from
	@Kombinacio K
	inner join Szamlafej F on F.OsszAr = K.OsszAr

	union all

	select
	K.Id, K.ElozoId, K.SzamlaTetelId, E.OsszAr
	from
	Elozmeny E
	inner join @Kombinacio K on E.ElozoId = K.Id
)
select SzamlaTetelId = T.ID ,Szamlaszam = F.Szamlaszam ,Ar = T.Ar ,Ado = F.Ado
from Elozmeny E
inner join Szamlafej F on F.OsszAr = E.OsszAr
inner join Szamlatetel T on T.ID = E.SzamlaTetelId
order by
E.OsszAr, T.ID

Ez egy olyan feladat, amin érdemes pár percig gondolkodni, és rácsodálkozni, hogy még egy ilyen ciklusért ordító feladatot is meg lehet oldani CTE-kkel (a hatékonyság persze más kérdés).

HTTP 2 a Windows 10-ben

Sunday, January 25th, 2015

A Windows 10 IIS-e és a benne levő IE is tudja a http2-t, egyelőre még csak SSL-en.

A http 2 nagyon jópofának néz ki, sok stream egy tcp csatornán, kérések priorizálása menet közben, a szerver küldhet kérés nélkül is plusz tartalmat a kliensnek (pl. css-eket vagy js-eket), stb. Valszeg elég sok idő lesz, mire elterjed, de webfejlesztők szeretni fogják, és a websiteok is sokkal gyorsabban be fognak jönni a böngészőben.

.NET fejtörő 3.

Thursday, January 22nd, 2015

Update: bocs, a Counter osztály lemaradt, így nem volt értelme a feladatnak.

Piros vagy zöld lesz a teszt kimenete? Válaszokat indoklással kommentben várom. A hozzászólások moderálva vannak, hogy 2 napot tudjam késleltetni a válaszokat, így mindenkinek lesz ideje gondolkodni. Jó filózást!


struct Counter
{
    int counter;
    public override string ToString()
    {
        return counter++.ToString(CultureInfo.InvariantCulture);
    }
}

[TestMethod]
public void Teaser3()
{
    var sb = new StringBuilder();
            
    var sz = new Counter();
            
    sb.Append(sz);
    Object p = sz;
    Object o = p;
    sb.Append(sz);
    sb.Append(o);
    sb.Append(p);
    sb.Append(o);

    Assert.AreEqual("01234", sb.ToString());
}

A Test Driven Development tanfolyam következő felvonása 2015. február kilencedikén lesz, szeretettel várlak.

SQL fejtörő 5. megoldás

Tuesday, January 20th, 2015

Egyszerű csoportosítós példa.

A legkézenfekvőbb megoldás egy sima group by és min:

select E.Nev, Telefon = min(E.Telefon) from Ember E group by E.Nev

Ezt egy covering nc indexszel szépen fel lehet gyorsítani:

CREATE NONCLUSTERED INDEX [IX_1] ON [dbo].[Ember]
(
	[Nev] ASC
)
INCLUDE ([Telefon]) 

Egy másik megoldás már költségesebb, de helyes eredményt ad (a komment szerzője szerintem ismerte az elsőt, csak valami trükkösebb megoldást akart adni):

select *
from Ember e1
where not exists (
select *
from Ember e2
where e1.Nev = e2.Nev
and e1.Telefon > e2.Telefon
)

Ennek logikája már körülményesebb, kérem azokat a sorokat, amelyekhez nem létezik olyan sor, aminek ugyanaz a neve, de kisebb a telefonszáma. Ezt csak joinnal tudja megoldani a szerver, jelentősen nagyobb költséggel.

Egy jelentősen más logikájú megoldás is érkezett:

;WITH Data AS
(
	SELECT Nev, Telefon, ROW_NUMBER() OVER (PARTITION BY Nev ORDER BY Telefon) AS RowNumber
	FROM Ember
)
SELECT Nev, Telefon
FROM Data
WHERE RowNumber = 1

Felsorszámozzuk a sorokat a Telefon szerint rendezve, partícionálva a Nev alapján, majd ebből a halmazból kiszedjük azokat a sorokat, amelyek sorszáma 1. Minden Nev-hez 1 ilyen lesz, értelemszerűen.
Ez a megoldás nem szereti az előbbi indexünket. Ahhoz, hogy ez is gyors legyen, a Telefon oszlopot is bele kell rakni az index kulcsai közé:

CREATE NONCLUSTERED INDEX [IX_1] ON [dbo].[Ember]
(
	[Nev] ASC,
	[Telefon] ASC
)

Így a group by-os és ezen megoldás költsége már azonos lesz, bár a tervük nem. Ez esetben sok sor esetén lehetne eldönteni, van-e perf különbség a két megoldás között, akinek van erre ideje, érdekelne az eredménye. Mondjuk 10 millió sorra. Érzésre nem lesz, de cáfoljatok meg, konkrét terveket mutatva.

Köszönöm mindenkinek a megoldásokat, és külön köszönöm Molnár Csabának, hogy a nem nyilvánvaló megoldást is megmutatta.

.NET fejtörő 1. megoldás

Tuesday, January 20th, 2015

Ez a feladat egyszerű volt. A .NET-es lebegőpontos számok ismerik a végtelen fogalmát, így a nullával osztás végtelent ad eredményül. Azért lesz lebegőpontos az osztás, mert a PI konstans double, így az a értéke is double lesz. A b ugyan int lesz, de a compiler átkonvertálja double-lé, mielőtt az osztást elvégezné, mivel a másik argumentum double. Miért?
A C# spec 7.3.6 Numeric promotions bekezdése alapján azt az overloadolt operátor + verziót választja ki, amelyik passzol a paraméterekre. Nincs int, dobule verzió, ezért konvertálnia kell. A double -> int nem implicit konverzió, így megpróbálkozik az int -> double-lel, azaz a második argumentumot átkonvertálja double-lé. Erre pedig már passzol a double operator +(double x, double y) overloadolt alak.

.NET fejtörő 2.

Tuesday, January 20th, 2015

Piros vagy zöld lesz a teszt kimenete? Válaszokat indoklással kommentben várom. A hozzászólások moderálva vannak, hogy 2 napot tudjam késleltetni a válaszokat, így mindenkinek lesz ideje gondolkodni. Jó filózást!

[TestMethod]
public void Teaser2()
{
    StringBuilder sb = new StringBuilder();

    for (int i = 0; i < 10; i++)
    {
        sb.Append(i + ' ');
    }

    Assert.AreEqual("1 2 3 4 5 6 7 8 9", sb.ToString());
}

A Test Driven Development tanfolyam következő felvonása február kilencedikén lesz, szeretettel várlak.

HashCat

Sunday, January 18th, 2015

Kipróbáltam a GPU alapú HashCatet egy erős desktop gépen. A 6 karakteres, csak kisbetűs SHA1-gyel salt nélkül hashelt jelszavakat brute force módon kb. 1mp alatt töri meg. Ha még hozzávettem számokat a végére, xxxxxx12, azt 1 perc alatt törte meg.
Durva, MD5-ből 5 milliárdot tör meg 1 mp alatt. SHA1-ből is milliárd felett.

TDD emlékeztető.

SQL fejtörő 6.

Friday, January 16th, 2015

Ez a fejtörő most sokkal nehezebb lesz. Adottak számlák és számla tételek. A számla tételek különböző ÁFA kategóriákba tartoznak. Elveszett viszont, hogy melyik tétel melyik ÁFA kategóriába tartozik. Csak összesítve tudjuk, hogy egy adott számlán mennyi volt az egyes ÁFA tételű termékek össz értéke. A feladat visszaállítani minden egyes tétel ÁFA kulcsát. Az adatok úgy vannak összerakva, hogy a feladat egyértelműen megoldható legyen.

Bemeneti adatok:

Számlák:

ID	Szamlaszam	OsszAr	Ado
1	Szamla1	500.00	Afa27
2	Szamla1	300.00	Afa16
3	Szamla2	4000.00	Afa38

Számla tételek:

ID	Szamlaszam	Ar
1	Szamla1	20.00
2	Szamla1	111.00
3	Szamla1	250.00
4	Szamla1	20.00
5	Szamla1	15.00
6	Szamla1	189.00
7	Szamla1	100.00
8	Szamla1	95.00
9	Szamla2	1000.00
10	Szamla2	3000.00

Elvárt kimenet, Adó, számlaszám és id szerint van rendezve:

id	Szamlaszam	Ar	Ado
2	Szamla1	111.00	Afa16
6	Szamla1	189.00	Afa16
1	Szamla1	20.00	Afa27
3	Szamla1	250.00	Afa27
4	Szamla1	20.00	Afa27
5	Szamla1	15.00	Afa27
7	Szamla1	100.00	Afa27
8	Szamla1	95.00	Afa27
9	Szamla2	1000.00	Afa38
10	Szamla2	3000.00	Afa38

Többféle megoldás is adható, mindegyik legalább 15 soros.

use tempdb;

IF OBJECT_ID('Szamlatetel','U') IS NOT NULL BEGIN
  DROP TABLE Szamlatetel
END 
GO

CREATE TABLE Szamlatetel(
	[ID] [INT] IDENTITY(1,1) NOT NULL,
	Szamlaszam [VARCHAR](20) NOT NULL,
	Ar [DECIMAL](18,2) NOT NULL	
)
GO

INSERT INTO Szamlatetel(Szamlaszam,Ar) 
SELECT 'Szamla1', 20 UNION ALL
SELECT 'Szamla1', 111 UNION ALL
SELECT 'Szamla1', 250 UNION ALL
SELECT 'Szamla1', 20  UNION ALL
SELECT 'Szamla1', 15  UNION ALL
SELECT 'Szamla1', 189 UNION ALL
SELECT 'Szamla1', 100 UNION ALL
SELECT 'Szamla1', 95 UNION ALL
SELECT 'Szamla2', 1000 UNION ALL
SELECT 'Szamla2', 3000
go

IF OBJECT_ID('Szamlafej','U') IS NOT NULL BEGIN
  DROP TABLE Szamlafej
END 
GO

CREATE TABLE Szamlafej(
	[ID] [INT] IDENTITY(1,1) NOT NULL,
	Szamlaszam [VARCHAR](20) NOT NULL,
	OsszAr [DECIMAL](18,2) NOT NULL DEFAULT(0),
	[Ado] [VARCHAR](5) NOT NULL
)
GO

INSERT INTO Szamlafej(Szamlaszam, OsszAr, Ado) 
SELECT 'Szamla1', 500, 'Afa27' UNION ALL
SELECT 'Szamla1', 300, 'Afa16' UNION ALL
SELECT 'Szamla2', 4000,  'Afa38'

SELECT * FROM Szamlafej;
GO
SELECT * FROM Szamlatetel order by id;
GO

A megfejtéseket szokás szerint kommentben várom, amelyeket ezúttal a feladat nehézsége miatt 5 nap múlva engedek ki.

A Test Driven Development tanfolyam következő felvonása február kilencedikén lesz, szeretettel várom az érdeklődőket.

EF WithRequiredDependent – mit csinál ez?

Thursday, January 15th, 2015

Nem jövök rá, pontosan mit is csinál ez? A HasRequired már önmagában megcsinálja a foreign keyt, mit ad ez még a modellhez?

.NET fejtörő 1.

Thursday, January 15th, 2015

Piros vagy zöld lesz a teszt kimenete? Válaszokat indoklással kommentben várom. A hozzászólások moderálva vannak, hogy 2 napot tudjam késleltetni a válaszokat, így mindenkinek lesz ideje gondolkodni. Jó filózást!

[TestMethod]
public void Teaser1()
{
    try
    {
        var a = Math.PI;
        var b = 0;
        var c = a / b;
    }
    catch (DivideByZeroException exception)
    {
        Assert.Fail("A kutya fáját");
    }
}

A Test Driven Development tanfolyam következő felvonása 2015. február kilencedikén lesz, szeretettel várlak.

SQL fejtörő 5.

Tuesday, January 13th, 2015

Egyszerű lekérdezéses feladat:

use tempdb;

create table Ember
(
	Nev nvarchar(50) not null,
	Telefon nvarchar(50) not null
);
go

insert Ember(Nev, Telefon)
values 
('Gizi', '22222222'),
('Gizi', '00000000'),
('Géza', '11111111'),
('Mari', '33333333');
go

--Ide jön a te query-d:

--A lekérdezés elvárt kimenetében minden név egyszer szerepel, és mindhez egy telefonszám, az, amelyik abc sorrendben az első:
--Géza	11111111
--Gizi	00000000
--Mari	33333333

drop table Ember;

A megfejtéseket szokás szerint kommentben várom, amelyeket 2 nap múlva engedek ki.

A Test Driven Development tanfolyam következő felvonása február kilencedikén lesz, szeretettel várom az érdeklődőket.

SQL fejtörő 4. megoldás

Monday, January 12th, 2015

Meglepő módon csak 1 megoldás érdekezett erre a fejtörőre, azt hittem egy ez sima guglizós példa lesz.

A példában egy login trigger volt, ami minden login kísérletet visszautasít, azaz ezzel kizárhatjuk magunkat is a szerver példányról, hiába vagyunk sysadminok.

A legegyszerűbb megoldás ilyenkor a 2005-ben bevezetett Dedicated Admin Connection. Simán a szerver neve elé be kell írni, hogy ADMIN:, és máris be tudunk menni a szerverre, hogy töröljük a triggert.

Köszönöm Atcomnak a megoldást, aki egy másik módszert is leírt:
“az SQL Server instanciát minimális konfigurációs módban indítjuk el (-f startup option), ilyenkor az SQL kiszolgáló “single-user” módban kerül.”

Ez már macerásabb, inkább ez durvább problémáknál szoktuk használni, de mindenképpen jó tudni róla.

Delayed Durability sebességnövekedés

Sunday, January 11th, 2015

Kipróbáltam az SQL Server 2014-ben megjelent Delayed Durability-t többféle scenarioban, ezzel a scripttel.
Laptopon, SSD háttérrel de kevés RAM-mal (8G) nem igazán okozott sebességnövekedést. Valaki használja már ezt élőben, rendes diszkekkel? Érdekelne, ott mennyit javít.

Gyors .NET tömörítő algoritmusok

Sunday, January 11th, 2015

A backtesteremben nagyon sok adatot kell kezelni memóriában, sok gigabájtot. A memóriacache-emben tömörítve vannak az adatok. Tömörítésre eddig a .NET GZip algoritmusát használtam, fastest módban, így elfogadható sebességet és tömörítést kapva.
Most viszont olvastam róla, hogy vannak alternatív algoritmusok is. Tettem hát egy próbát, az alábbi eredményeket kapva:

.NET GZipStream fastest mode:
Compression time: 00:00:00.1801551, compressed size: 75,122.00
Decompression time: 00:00:00.2001859

Ez a baseline.

Snappy native:
https://snappy4net.codeplex.com/
Compression time: 00:00:00.1815305, compressed size: 508,387.00
Decompression time: 00:00:00.1464451

Hát, ez nem győzött meg.

Lz4:
http://lz4net.codeplex.com/
Compression time: 00:00:00.6303679, compressed size: 41,926.00
Decompression time: 00:00:00.1364594

Ez se az igazi sebességben, de legalább jól tömörít.

Lz4Native:
https://code.google.com/p/lz4-net/
Compression time: 00:00:00.0925361, compressed size: 49,862.00
Decompression time: 00:00:00.1757730

Na, ez már tetszik. Jobban tömörített, mint a gzip, fele annyi idő alatt, mint az, és a decompress is egy kicsit gyorsabb. Kipróbálom majd igazi adatokkal is, meglátjuk, ott hogy zenél.

SQL fejtörő 3. megoldások

Friday, January 9th, 2015

Az előző fejtörőben ez volt a kérdés:
“Hogyan biztosítanád egy SQL Server táblában, hogy egy oszlop csak egyedi értékeket tartalmazzon, de NULL-ból bármennyi lehessen benne?”

A unique contranint vagy index alapban nem engedi meg a duplikált nullokat, de SQL Server 2008-tól van filtered index, ami segítségével egy where feltételt lehet megfogalmazni az indexelendő adatokra. Ezzel már passzol a unique index a feladatra.

[soruce lang=’sql]
CREATE UNIQUE INDEX IX_Oszlop1 ON AlmaTabla (Oszlop1) WHERE Oszlop1 IS NOT NULL
[/source]

SQL fejtörő 4.

Friday, January 9th, 2015

Ez most könnyű lesz. Tegyük fel ügyetlen voltam, és lefuttattam az alábbi kódot. Nincs is megnyitott kapcsolatom a szerverre. Mit tehetek? Reinstall?

CREATE TRIGGER AFrancbaTrigger
ON ALL SERVER
FOR LOGON
AS
BEGIN
    ROLLBACK;
END;

A megfejtéseket szokás szerint kommentben várom, amelyeket 2 nap múlva engedek ki.

A Test Driven Development tanfolyam következő felvonása február kilencedikén lesz, szeretettel várlak.

Invalid file karakterek lecserélése

Wednesday, January 7th, 2015

Egy webes munkában file nevet kellett generálni a letöltendő fájloknak. Az eredeti névben lehet kettőspont, stb. ami nem megengedett fájlnevekben. A böngésző is lekezeli ezt valahogy, de én akartam ezt előre megoldani.

Itt vannak megoldások a feladatra.

Sokféle megoldást adnak, nekem a linq-s tetszik legjobban:

var invalidChars = Path.GetInvalidFileNameChars();
var invalidCharsRemoved = stringWithInvalidChars.Where(x => !invalidChars.Contains(x)).ToArray();

De ez a legfurább logikájú, ez ütötte meg a szememet:

string cleanFileName = String.Join("", fileName.Split(Path.GetInvalidFileNameChars()));

Erre mondja az angol, hogy convoluted logic?

Test Driven Development tanfolyam: február 9.

SQL fejtörő 2. megoldások

Wednesday, January 7th, 2015

Köszönöm a beérkezett megoldásokat a feladatra. Hárman a left joinra építették a megoldást, a bal oldali táblát felszaporítva két sorra, így az eredményhalmaz is két soros lett.
Egy másik megoldás is érkezett, ez ezért tetszik, mivel teljesen másként közelítette meg a feladatot.
select null union all-lal egyszerűen hozzácsapott még egy nullos sort a kimenethez. Ügyes. :)

Közben kiraktam a következő fejtörőt is, fel lehet venni a kesztyűt.

SQL fejtörő 3.

Wednesday, January 7th, 2015

Hogyan biztosítanád egy SQL Server táblában, hogy egy oszlop csak egyedi értékeket tartalmazzon, de NULL-ból bármennyi lehessen benne?

USE TempDB
GO

set ansi_nulls on;
go
CREATE TABLE AlmaTabla (Oszlop1 INT NULL);
GO

--------------------------------------
-- Ide kellene valami, amitol az alabb lathato eredmenyek szuletnek
--------------------------------------

--Elvart eredmeny:

INSERT INTO AlmaTabla (Oszlop1) VALUES (1)		--ok
GO
INSERT INTO AlmaTabla (Oszlop1) VALUES (2)		--ok
GO
INSERT INTO AlmaTabla (Oszlop1) VALUES (NULL)	--ok
GO
INSERT INTO AlmaTabla (Oszlop1) VALUES (NULL)	--ok
GO
INSERT INTO AlmaTabla (Oszlop1) VALUES (2)		--exception
GO

DROP TABLE AlmaTabla;
GO

A Test Driven Development tanfolyam következő felvonása február kilencedikén lesz, szeretettel várlak.

SQL telepítés rendberakása

Tuesday, January 6th, 2015

Kevés volt a hely a gép vinyóján, ezért, minden tiltás ellenére kitöröltem a c:\windows\installer könyvár tartalmát. Ez egy hónapig nem is okozott gondot, amíg a hétvégén hozzá nem akartam adni egy új komponenst az SQL Server 2014-hez.
A setup hiányolta az msi fájlokat a c:\windows\installer könyvtárban. De nem olyan egyszerű ezeket pótolni, mivel nem az eredeti néven szerepelnek a misik benne. A registryben meg lehet őket találni, majd onnan a nevük segítségével meg lehet próbálni megtalálni az eredeti msiket az SQL telepítő DVD-n. Ezeket lefuttatva helyre lehet rakni őket, a telepítők bemásolják a c:\windows\installer-be a szükséges fájlokat. De persze nem ilyen egyszerű az élet. Több órai szívás után feladtam, és leinstalláltam a szervert. Persze, ezek után már nem megy fel egy új példány. Sok kis darabka ott marad még az uninstall után, ami megzavarja a telepítőt. Itt van egy leírás, hogyan lehet levakarni a maradványokat. Mivel kézzel kellene sokat matyizni vele, írtam egy kis powershell scriptet a cikk alapján.

$oXMLDocument = Get-Content -Path "C:\Program Files\Microsoft SQL Server\120\Setup Bootstrap\Log\20150105_092215\Datastore\Datastore_Discovery.xml" 
$oXMLDocument.SelectNodes("//@MsiId") | select Value | %{ msiexec /quiet /passive /x $_.Value}

!!! Vigyázat, ez szemrebbenés nélkül levakarja a telepített SQL Server példányokat!!! Ésszel tessék futtatni, és csak dev gépen, ha gáz van.

De ez még mindig kevés volt, a perf countereket telepítő ininiket a telepítő nem másolta be a telepített SQL könyvtárba, így továbbra is beakadt a telepítő. sqlagtctr.ini és sqlctr.ini a két fájl. Egyik fenn van közvetlenül a telepítő DVD-n, a másik egy msiben van, amit ki kell csomagolni a felmásoláshoz:

msiexec /a sql_engine_core_inst_loc.msi  /qb TARGETDIR=c:\temp\misi2

Ezek után se volt még sok minden ok, pl. az SQLWriter nem tudott feltelepülni. Ezen meg egy sfc /scannow segített, elég sokat kellett neki rendbe rakni.

Még ezek után se ment fel hiba nélkül a szerver, valami WMI problémája még volt, de emiatt már szerencsére nem görgette vissza a telepítést.
Huh.
De, a Management Studio viszont visszagörgette magát, a WMI MOF hiba miatt.
(“The MOF Compiler could not connect with the WMI Server. This is either because of a semantic error such as an incompatibility with the existing WMI repository or an actual error such as the failure of the WMI Server to start”.)

WMIDiag letölt, átír a kommentek alapján, hogy menjen Win 8.1-en is.

Ebből kiderült, hogy 1 file hiányzott az sql telepítésből:
WARNING: Some WMI providers EXE/DLL file(s) are missing: …………………………………………………… 1 WARNING(S)!
60906 23:04:13 (0) ** – ROOT/MICROSOFT/SQLSERVER/COMPUTERMANAGEMENT12, MSSQL_ManagementProvider, C:\Program Files\Microsoft SQL Server\120\Shared\sqlmgmprovider.dll

Ezt bemásoltam neki kézzel.

Emellett újraélesztettem az össze MOF-ját a C:\Program Files\Microsoft SQL Server\MSSQL12.MSSQLSERVER\MSSQL\Binn könyvtárból a mofcomp.exe segítségével.
Az egyiknek nem mof a kiterjesztése, mivel azt átírja a telepítő az instance névre:
mofcomp Sqlwep-uni.mof.transformed

Aztán SQL install repair újra, még egy hiányzó msi futattása, hogy az installer könyvtárban még mindig hiányzi misit pótolja.

A fenti lépéseket persze nem csak egyszer kellett megtenni, és nem pont ebben a sorrendben.

És végül győzelem, minden pipa zöld a repair után. :)

2 napot elvitt az életemből, feleslegesen. Ne csináljátok utánam.

Tanulságok:
1. Ne piszkáljuk a c:\windows\installer könyvtárat, mert napokat fog elvinni a kijavítása.
2. Ne vegyünk laptopot 120 G-s SSD-vel, minimum 256G kell egy fejlesztői gépbe, főleg, ha egy-két VM is van rajta.
3. A WmiDiag hasznos eszköz, ez nélkül nem tudtam volna megjavítani a telepítést.